<commit_before>package release

import (
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/google/go-github/github"
	"github.com/goreleaser/releaser/config"
	"github.com/goreleaser/releaser/uname"
	"golang.org/x/oauth2"
	"os/exec"
)

func Release(version, diff string, config config.ProjectConfig) error {
	fmt.Println("Creating release", version, "on repo", config.Repo, "...")
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: config.Token},
	)
	tc := oauth2.NewClient(context.Background(), ts)
	client := github.NewClient(tc)

	owner := strings.Split(config.Repo, "/")[0]
	repo := strings.Split(config.Repo, "/")[1]
	releaseData := &github.RepositoryRelease{
		Name:            github.String(version),
		TagName:         github.String(version),
		Body:            github.String(description(diff)),
	}
	r, _, err := client.Repositories.CreateRelease(owner, repo, releaseData)
	if err != nil {
		return err
	}
	for _, system := range config.Build.Oses {
		for _, arch := range config.Build.Arches {
			if err := upload(client, *r.ID, owner, repo, system, arch, config.BinaryName); err != nil {
				return err
			}
		}
	}
	return nil
}

func description(diff string) string {
	result := "Changelog:\n" + diff + "\n\nAutomated with @goreleaser"
	cmd := exec.Command("go", "version")
	bts, err := cmd.CombinedOutput()
	if err != nil {
		return result
	}
	return result + "\nBuilt with " + string(bts)
}

func upload(client *github.Client, releaseID int, owner, repo, system, arch, binaryName string) error {
	name := binaryName + "_" + uname.FromGo(system) + "_" + uname.FromGo(arch) + ".tar.gz"
	file, err := os.Open("dist/" + name)
	if err != nil {
		return err
	}
	defer file.Close()
	fmt.Println("Uploading", file.Name(), "...")
	_, _, err = client.Repositories.UploadReleaseAsset(owner, repo, releaseID, &github.UploadOptions{
		Name: name,
	}, file)
	return err
}
<commit_msg>multiple uploads<commit_after>package release

import (
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/google/go-github/github"
	"github.com/goreleaser/releaser/config"
	"github.com/goreleaser/releaser/uname"
	"golang.org/x/oauth2"
	"os/exec"
	"golang.org/x/sync/errgroup"
)

func Release(version, diff string, config config.ProjectConfig) error {
	fmt.Println("Creating release", version, "on repo", config.Repo, "...")
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: config.Token},
	)
	tc := oauth2.NewClient(context.Background(), ts)
	client := github.NewClient(tc)

	owner := strings.Split(config.Repo, "/")[0]
	repo := strings.Split(config.Repo, "/")[1]
	releaseData := &github.RepositoryRelease{
		Name:            github.String(version),
		TagName:         github.String(version),
		Body:            github.String(description(diff)),
	}
	r, _, err := client.Repositories.CreateRelease(owner, repo, releaseData)
	if err != nil {
		return err
	}
	var g errgroup.Group
	for _, system := range config.Build.Oses {
		for _, arch := range config.Build.Arches {
			system := system
			arch := arch
			g.Go(func() error {
				return upload(client, *r.ID, owner, repo, system, arch, config.BinaryName)
			})
		}
	}
	return g.Wait()
}

func description(diff string) string {
	result := "Changelog:\n" + diff + "\n\nAutomated with @goreleaser"
	cmd := exec.Command("go", "version")
	bts, err := cmd.CombinedOutput()
	if err != nil {
		return result
	}
	return result + "\nBuilt with " + string(bts)
}

func upload(client *github.Client, releaseID int, owner, repo, system, arch, binaryName string) error {
	name := binaryName + "_" + uname.FromGo(system) + "_" + uname.FromGo(arch) + ".tar.gz"
	file, err := os.Open("dist/" + name)
	if err != nil {
		return err
	}
	defer file.Close()
	fmt.Println("Uploading", file.Name(), "...")
	_, _, err = client.Repositories.UploadReleaseAsset(owner, repo, releaseID, &github.UploadOptions{
		Name: name,
	}, file)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd-operator 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 (
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/url"
	"os"
	"testing"
	"time"

	"github.com/coreos/etcd-operator/pkg/spec"
	"github.com/coreos/etcd-operator/test/e2e/framework"
	"github.com/coreos/etcd/embed"
	"github.com/coreos/etcd/pkg/netutil"

	"k8s.io/client-go/1.5/pkg/api/unversioned"
	"k8s.io/client-go/1.5/pkg/api/v1"
)

func TestSelfHosted(t *testing.T) {
	t.Run("self hosted", func(t *testing.T) {
		t.Run("create self hosted cluster from scratch", testCreateSelfHostedCluster)
		t.Run("migrate boot member to self hosted cluster", testCreateSelfHostedClusterWithBootMember)
	})
}

func testCreateSelfHostedCluster(t *testing.T) {
	if os.Getenv(envParallelTest) == envParallelTestTrue {
		t.Parallel()
	}
	f := framework.Global
	c := newClusterSpec("test-etcd-", 3)
	c = clusterWithSelfHosted(c, &spec.SelfHostedPolicy{})
	testEtcd, err := createCluster(t, f, c)
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(t, f, testEtcd); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(t, f, testEtcd.Name, 3, 240*time.Second); err != nil {
		t.Fatalf("failed to create 3 members self-hosted etcd cluster: %v", err)
	}
}

func testCreateSelfHostedClusterWithBootMember(t *testing.T) {
	if os.Getenv(envParallelTest) == envParallelTestTrue {
		t.Parallel()
	}
	dir, err := ioutil.TempDir("", "embed-etcd")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	host, _ := netutil.GetDefaultHost()
	port := rand.Intn(61000-32768) + 32768 // linux ephemeral ports

	embedCfg := embed.NewConfig()
	embedCfg.Dir = dir
	lpurl, _ := url.Parse(fmt.Sprintf("http://%s:%d", host, port+1))
	lcurl, _ := url.Parse(fmt.Sprintf("http://%s:%d", host, port))
	embedCfg.LCUrls = []url.URL{*lcurl}
	embedCfg.LPUrls = []url.URL{*lpurl}

	apurl, _ := url.Parse(fmt.Sprintf("http://%s:%d", host, port+1))
	acurl, _ := url.Parse(fmt.Sprintf("http://%s:%d", host, port))
	embedCfg.ACUrls = []url.URL{*acurl}
	embedCfg.APUrls = []url.URL{*apurl}
	embedCfg.InitialCluster = "default=" + apurl.String()

	e, err := embed.StartEtcd(embedCfg)
	if err != nil {
		t.Fatal(err)
	}
	defer e.Close()

	select {
	case <-e.Server.ReadyNotify():
	case <-time.After(30 * time.Second):
		t.Fatal("timeout: wait etcd server ready")
	}

	t.Log("etcdserver is ready")

	f := framework.Global

	c := newClusterSpec("test-etcd-", 3)
	c = clusterWithSelfHosted(c, &spec.SelfHostedPolicy{
		BootMemberClientEndpoint: embedCfg.ACUrls[0].String(),
	})
	testEtcd, err := createCluster(t, f, c)
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(t, f, testEtcd); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(t, f, testEtcd.Name, 3, 120*time.Second); err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
	}
}

func makeSelfHostedEnabledCluster(genName string, size int) *spec.EtcdCluster {
	return &spec.EtcdCluster{
		TypeMeta: unversioned.TypeMeta{
			Kind:       "EtcdCluster",
			APIVersion: "coreos.com/v1",
		},
		ObjectMeta: v1.ObjectMeta{
			GenerateName: genName,
		},
		Spec: &spec.ClusterSpec{
			Size:       size,
			SelfHosted: &spec.SelfHostedPolicy{},
		},
	}
}
<commit_msg>e2e: remove unused method<commit_after>// Copyright 2016 The etcd-operator 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 (
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/url"
	"os"
	"testing"
	"time"

	"github.com/coreos/etcd-operator/pkg/spec"
	"github.com/coreos/etcd-operator/test/e2e/framework"
	"github.com/coreos/etcd/embed"
	"github.com/coreos/etcd/pkg/netutil"
)

func TestSelfHosted(t *testing.T) {
	t.Run("self hosted", func(t *testing.T) {
		t.Run("create self hosted cluster from scratch", testCreateSelfHostedCluster)
		t.Run("migrate boot member to self hosted cluster", testCreateSelfHostedClusterWithBootMember)
	})
}

func testCreateSelfHostedCluster(t *testing.T) {
	if os.Getenv(envParallelTest) == envParallelTestTrue {
		t.Parallel()
	}
	f := framework.Global
	c := newClusterSpec("test-etcd-", 3)
	c = clusterWithSelfHosted(c, &spec.SelfHostedPolicy{})
	testEtcd, err := createCluster(t, f, c)
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(t, f, testEtcd); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(t, f, testEtcd.Name, 3, 240*time.Second); err != nil {
		t.Fatalf("failed to create 3 members self-hosted etcd cluster: %v", err)
	}
}

func testCreateSelfHostedClusterWithBootMember(t *testing.T) {
	if os.Getenv(envParallelTest) == envParallelTestTrue {
		t.Parallel()
	}
	dir, err := ioutil.TempDir("", "embed-etcd")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	host, _ := netutil.GetDefaultHost()
	port := rand.Intn(61000-32768) + 32768 // linux ephemeral ports

	embedCfg := embed.NewConfig()
	embedCfg.Dir = dir
	lpurl, _ := url.Parse(fmt.Sprintf("http://%s:%d", host, port+1))
	lcurl, _ := url.Parse(fmt.Sprintf("http://%s:%d", host, port))
	embedCfg.LCUrls = []url.URL{*lcurl}
	embedCfg.LPUrls = []url.URL{*lpurl}

	apurl, _ := url.Parse(fmt.Sprintf("http://%s:%d", host, port+1))
	acurl, _ := url.Parse(fmt.Sprintf("http://%s:%d", host, port))
	embedCfg.ACUrls = []url.URL{*acurl}
	embedCfg.APUrls = []url.URL{*apurl}
	embedCfg.InitialCluster = "default=" + apurl.String()

	e, err := embed.StartEtcd(embedCfg)
	if err != nil {
		t.Fatal(err)
	}
	defer e.Close()

	select {
	case <-e.Server.ReadyNotify():
	case <-time.After(30 * time.Second):
		t.Fatal("timeout: wait etcd server ready")
	}

	t.Log("etcdserver is ready")

	f := framework.Global

	c := newClusterSpec("test-etcd-", 3)
	c = clusterWithSelfHosted(c, &spec.SelfHostedPolicy{
		BootMemberClientEndpoint: embedCfg.ACUrls[0].String(),
	})
	testEtcd, err := createCluster(t, f, c)
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(t, f, testEtcd); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(t, f, testEtcd.Name, 3, 120*time.Second); err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Collins
//
// 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 ui

type mode int

const (
	mScaled mode = iota
	mUnscaled
)

type AttachPoint int

const (
	Top, Left AttachPoint = iota, iota
	Middle, Center
	Bottom, Right
)
<commit_msg>render/ui: add missing docs<commit_after>// Copyright 2015 Matthew Collins
//
// 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 ui

type mode int

const (
	mScaled mode = iota
	mUnscaled
)

// AttachPoint is a side of the screen that an element
// can be attached to
type AttachPoint int

// Attachment points
//     VAlign, HAlign
const (
	Top, Left AttachPoint = iota, iota
	Middle, Center
	Bottom, Right
)
<|endoftext|>


<commit_before>// main simply contains the primary web serving code that allows peers to
// register and unregister as give mode peers running within the Lantern
// network
package main

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/getlantern/keyman"
	"github.com/getlantern/tlsdefaults"
)

const (
	PKFile   = "pk.pem"
	CertFile = "cert.pem"
)

func startHttp() {
	http.HandleFunc("/register", register)
	http.HandleFunc("/unregister", unregister)
	laddr := fmt.Sprintf(":%d", *port)

	tlsConfig := tlsdefaults.Server()
	_, _, err := keyman.StoredPKAndCert(PKFile, CertFile, "Lantern", "localhost")
	if err != nil {
		log.Fatalf("Unable to initialize private key and certificate: %v", err)
	}
	cert, err := tls.LoadX509KeyPair(CertFile, PKFile)
	if err != nil {
		log.Fatalf("Unable to load certificate and key from %s and %s: %s", CertFile, PKFile, err)
	}
	tlsConfig.Certificates = []tls.Certificate{cert}

	log.Debugf("About to listen at %v", laddr)
	l, err := tls.Listen("tcp", laddr, tlsConfig)
	if err != nil {
		log.Fatalf("Unable to listen for tls connections at %s: %s", laddr, err)
	}

	log.Debug("About to serve")
	http.Serve(l, nil)
}

// register is the entry point for peers registering themselves with the service.
// If peers are successfully vetted, they'll be added to the DNS round robin.
func register(resp http.ResponseWriter, req *http.Request) {
	name, ip, port, err := getHostInfo(req)
	if err == nil && port != 443 {
		err = fmt.Errorf("Port %d not supported, only port 443 is supported", port)
	}
	if name == "" {
		err = fmt.Errorf("Please specify a name")
	}
	if err != nil {
		resp.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(resp, err.Error())
		return
	}

	h := getOrCreateHost(name, ip)
	online, connectionRefused := h.status()
	if online {
		resp.WriteHeader(200)
		fmt.Fprintln(resp, "Connectivity to proxy confirmed")
		return
	}

	// Note this may not work across platforms, but the intent
	// is to tell the client if the connection was flat out
	// refused as opposed to timed out in order to allow them
	// to configure their router if possible.
	if connectionRefused {
		// 417 response code.
		resp.WriteHeader(http.StatusExpectationFailed)
		fmt.Fprintln(resp, "No connectivity to proxy - connection refused")
	} else {
		// 408 response code.
		resp.WriteHeader(http.StatusRequestTimeout)
		fmt.Fprintln(resp, "No connectivity to proxy - test request timed out")
	}
}

// unregister is the HTTP endpoint for removing peers from DNS. Peers are
// unregistered based on their ip (not their name).
func unregister(resp http.ResponseWriter, req *http.Request) {
	_, ip, _, err := getHostInfo(req)
	if err != nil {
		resp.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(resp, err.Error())
		return
	}

	h := getHostByIp(ip)
	msg := "Host not registered"
	if h != nil {
		h.unregister()
		msg = "Host unregistered"
	}
	resp.WriteHeader(200)
	fmt.Fprintln(resp, msg)
}

func getHostInfo(req *http.Request) (name string, ip string, port int, err error) {
	name = req.FormValue("name")
	ip = clientIpFor(req)
	portString := req.FormValue("port")

	if portString != "" {
		port, err = strconv.Atoi(portString)
		if err != nil {
			err = fmt.Errorf("Received invalid port for %v - %v: %d", name, ip, portString)
		}
	}

	return
}

func clientIpFor(req *http.Request) string {
	// Client requested their info
	clientIp := req.Header.Get("X-Forwarded-For")
	if clientIp == "" {
		clientIp = strings.Split(req.RemoteAddr, ":")[0]
	}
	// clientIp may contain multiple ips, use the first
	ips := strings.Split(clientIp, ",")
	return strings.TrimSpace(ips[0])
}
<commit_msg>Added error handling for http.Serve<commit_after>// main simply contains the primary web serving code that allows peers to
// register and unregister as give mode peers running within the Lantern
// network
package main

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/getlantern/keyman"
	"github.com/getlantern/tlsdefaults"
)

const (
	PKFile   = "pk.pem"
	CertFile = "cert.pem"
)

func startHttp() {
	http.HandleFunc("/register", register)
	http.HandleFunc("/unregister", unregister)
	laddr := fmt.Sprintf(":%d", *port)

	tlsConfig := tlsdefaults.Server()
	_, _, err := keyman.StoredPKAndCert(PKFile, CertFile, "Lantern", "localhost")
	if err != nil {
		log.Fatalf("Unable to initialize private key and certificate: %v", err)
	}
	cert, err := tls.LoadX509KeyPair(CertFile, PKFile)
	if err != nil {
		log.Fatalf("Unable to load certificate and key from %s and %s: %s", CertFile, PKFile, err)
	}
	tlsConfig.Certificates = []tls.Certificate{cert}

	log.Debugf("About to listen at %v", laddr)
	l, err := tls.Listen("tcp", laddr, tlsConfig)
	if err != nil {
		log.Fatalf("Unable to listen for tls connections at %s: %s", laddr, err)
	}

	log.Debug("About to serve")
	err = http.Serve(l, nil)
	if err != nil {
		log.Fatalf("Unable to serve: %s", err)
	}
}

// register is the entry point for peers registering themselves with the service.
// If peers are successfully vetted, they'll be added to the DNS round robin.
func register(resp http.ResponseWriter, req *http.Request) {
	name, ip, port, err := getHostInfo(req)
	if err == nil && port != 443 {
		err = fmt.Errorf("Port %d not supported, only port 443 is supported", port)
	}
	if name == "" {
		err = fmt.Errorf("Please specify a name")
	}
	if err != nil {
		resp.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(resp, err.Error())
		return
	}

	h := getOrCreateHost(name, ip)
	online, connectionRefused := h.status()
	if online {
		resp.WriteHeader(200)
		fmt.Fprintln(resp, "Connectivity to proxy confirmed")
		return
	}

	// Note this may not work across platforms, but the intent
	// is to tell the client if the connection was flat out
	// refused as opposed to timed out in order to allow them
	// to configure their router if possible.
	if connectionRefused {
		// 417 response code.
		resp.WriteHeader(http.StatusExpectationFailed)
		fmt.Fprintln(resp, "No connectivity to proxy - connection refused")
	} else {
		// 408 response code.
		resp.WriteHeader(http.StatusRequestTimeout)
		fmt.Fprintln(resp, "No connectivity to proxy - test request timed out")
	}
}

// unregister is the HTTP endpoint for removing peers from DNS. Peers are
// unregistered based on their ip (not their name).
func unregister(resp http.ResponseWriter, req *http.Request) {
	_, ip, _, err := getHostInfo(req)
	if err != nil {
		resp.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(resp, err.Error())
		return
	}

	h := getHostByIp(ip)
	msg := "Host not registered"
	if h != nil {
		h.unregister()
		msg = "Host unregistered"
	}
	resp.WriteHeader(200)
	fmt.Fprintln(resp, msg)
}

func getHostInfo(req *http.Request) (name string, ip string, port int, err error) {
	name = req.FormValue("name")
	ip = clientIpFor(req)
	portString := req.FormValue("port")

	if portString != "" {
		port, err = strconv.Atoi(portString)
		if err != nil {
			err = fmt.Errorf("Received invalid port for %v - %v: %d", name, ip, portString)
		}
	}

	return
}

func clientIpFor(req *http.Request) string {
	// Client requested their info
	clientIp := req.Header.Get("X-Forwarded-For")
	if clientIp == "" {
		clientIp = strings.Split(req.RemoteAddr, ":")[0]
	}
	// clientIp may contain multiple ips, use the first
	ips := strings.Split(clientIp, ",")
	return strings.TrimSpace(ips[0])
}
<|endoftext|>


<commit_before>//Package codes is a helper package to help convert to transaction and operation result codes
//to strings used in horizon.
package codes

import (
	"github.com/go-errors/errors"
	"github.com/stellar/go-stellar-base/xdr"
)

var ErrUnknownCode = errors.New("Unknown result code")

//String returns the appropriate string representation of the provided result code
func String(code interface{}) (string, error) {
	switch code := code.(type) {
	case xdr.TransactionResultCode:
		switch code {
		case xdr.TransactionResultCodeTxSuccess:
			return "tx_success", nil
		case xdr.TransactionResultCodeTxFailed:
			return "tx_failed", nil
		case xdr.TransactionResultCodeTxTooEarly:
			return "tx_too_early", nil
		case xdr.TransactionResultCodeTxTooLate:
			return "tx_too_late", nil
		case xdr.TransactionResultCodeTxMissingOperation:
			return "tx_missing_operation", nil
		case xdr.TransactionResultCodeTxBadSeq:
			return "tx_bad_seq", nil
		case xdr.TransactionResultCodeTxBadAuth:
			return "tx_bad_auth", nil
		case xdr.TransactionResultCodeTxInsufficientBalance:
			return "tx_insufficient_balance", nil
		case xdr.TransactionResultCodeTxNoAccount:
			return "tx_no_source_account", nil
		case xdr.TransactionResultCodeTxInsufficientFee:
			return "tx_insufficient_fee", nil
		case xdr.TransactionResultCodeTxBadAuthExtra:
			return "tx_bad_auth_extra", nil
		case xdr.TransactionResultCodeTxInternalError:
			return "tx_internal_error", nil
		}
	case xdr.OperationResultCode:
		switch code {
		case xdr.OperationResultCodeOpInner:
			return "op_inner", nil
		case xdr.OperationResultCodeOpBadAuth:
			return "op_bad_auth", nil
		case xdr.OperationResultCodeOpNoAccount:
			return "op_no_source_account", nil
		}
	case xdr.CreateAccountResultCode:
		switch code {
		case xdr.CreateAccountResultCodeCreateAccountSuccess:
			return "op_success", nil
		case xdr.CreateAccountResultCodeCreateAccountMalformed:
			return "op_malformed", nil
		case xdr.CreateAccountResultCodeCreateAccountUnderfunded:
			return "op_underfunded", nil
		case xdr.CreateAccountResultCodeCreateAccountLowReserve:
			return "op_low_reserve", nil
		case xdr.CreateAccountResultCodeCreateAccountAlreadyExist:
			return "op_already_exists", nil
		}
	case xdr.PaymentResultCode:
		switch code {
		case xdr.PaymentResultCodePaymentSuccess:
			return "op_success", nil
		case xdr.PaymentResultCodePaymentMalformed:
			return "op_malformed", nil
		case xdr.PaymentResultCodePaymentUnderfunded:
			return "op_underfunded", nil
		case xdr.PaymentResultCodePaymentSrcNoTrust:
			return "op_src_no_trust", nil
		case xdr.PaymentResultCodePaymentSrcNotAuthorized:
			return "op_src_not_authorized", nil
		case xdr.PaymentResultCodePaymentNoDestination:
			return "op_no_destination", nil
		case xdr.PaymentResultCodePaymentNoTrust:
			return "op_no_trust", nil
		case xdr.PaymentResultCodePaymentNotAuthorized:
			return "op_not_authorized", nil
		case xdr.PaymentResultCodePaymentLineFull:
			return "op_line_full", nil
		case xdr.PaymentResultCodePaymentNoIssuer:
			return "op_no_issuer", nil
		}
	case xdr.PathPaymentResultCode:
		switch code {
		case xdr.PathPaymentResultCodePathPaymentSuccess:
			return "op_success", nil
		case xdr.PathPaymentResultCodePathPaymentMalformed:
			return "op_malformed", nil
		case xdr.PathPaymentResultCodePathPaymentUnderfunded:
			return "op_underfunded", nil
		case xdr.PathPaymentResultCodePathPaymentSrcNoTrust:
			return "op_src_no_trust", nil
		case xdr.PathPaymentResultCodePathPaymentSrcNotAuthorized:
			return "op_src_not_authorized", nil
		case xdr.PathPaymentResultCodePathPaymentNoDestination:
			return "op_no_destination", nil
		case xdr.PathPaymentResultCodePathPaymentNoTrust:
			return "op_no_trust", nil
		case xdr.PathPaymentResultCodePathPaymentNotAuthorized:
			return "op_not_authorized", nil
		case xdr.PathPaymentResultCodePathPaymentLineFull:
			return "op_line_full", nil
		case xdr.PathPaymentResultCodePathPaymentNoIssuer:
			return "op_no_issuer", nil
		case xdr.PathPaymentResultCodePathPaymentTooFewOffers:
			return "op_too_few_offers", nil
		case xdr.PathPaymentResultCodePathPaymentOfferCrossSelf:
			return "op_cross_self", nil
		case xdr.PathPaymentResultCodePathPaymentOverSendmax:
			return "op_over_source_max", nil
		}
	case xdr.ManageOfferResultCode:
		switch code {
		case xdr.ManageOfferResultCodeManageOfferSuccess:
			return "op_success", nil
		case xdr.ManageOfferResultCodeManageOfferMalformed:
			return "op_malformed", nil
		case xdr.ManageOfferResultCodeManageOfferSellNoTrust:
			return "op_sell_no_trust", nil
		case xdr.ManageOfferResultCodeManageOfferBuyNoTrust:
			return "op_buy_no_trust", nil
		case xdr.ManageOfferResultCodeManageOfferSellNotAuthorized:
			return "sell_not_authorized", nil
		case xdr.ManageOfferResultCodeManageOfferBuyNotAuthorized:
			return "buy_not_authorized", nil
		case xdr.ManageOfferResultCodeManageOfferLineFull:
			return "op_line_full", nil
		case xdr.ManageOfferResultCodeManageOfferUnderfunded:
			return "op_underfunded", nil
		case xdr.ManageOfferResultCodeManageOfferCrossSelf:
			return "op_cross_self", nil
		case xdr.ManageOfferResultCodeManageOfferSellNoIssuer:
			return "op_sell_no_issuer", nil
		case xdr.ManageOfferResultCodeManageOfferBuyNoIssuer:
			return "buy_no_issuer", nil
		case xdr.ManageOfferResultCodeManageOfferNotFound:
			return "op_offer_not_found", nil
		case xdr.ManageOfferResultCodeManageOfferLowReserve:
			return "op_low_reserve", nil
		}
	case xdr.SetOptionsResultCode:
		switch code {
		case xdr.SetOptionsResultCodeSetOptionsSuccess:
			return "op_success", nil
		case xdr.SetOptionsResultCodeSetOptionsLowReserve:
			return "op_low_reserve", nil
		case xdr.SetOptionsResultCodeSetOptionsTooManySigners:
			return "op_too_many_signers", nil
		case xdr.SetOptionsResultCodeSetOptionsBadFlags:
			return "op_bad_flags", nil
		case xdr.SetOptionsResultCodeSetOptionsInvalidInflation:
			return "op_invalid_inflation", nil
		case xdr.SetOptionsResultCodeSetOptionsCantChange:
			return "op_cant_change", nil
		case xdr.SetOptionsResultCodeSetOptionsUnknownFlag:
			return "op_unknown_flag", nil
		case xdr.SetOptionsResultCodeSetOptionsThresholdOutOfRange:
			return "op_threshold_out_of_range", nil
		case xdr.SetOptionsResultCodeSetOptionsBadSigner:
			return "op_bad_signer", nil
		case xdr.SetOptionsResultCodeSetOptionsInvalidHomeDomain:
			return "op_invalid_home_domain", nil
		}
	case xdr.ChangeTrustResultCode:
		switch code {
		case xdr.ChangeTrustResultCodeChangeTrustSuccess:
			return "op_success", nil
		case xdr.ChangeTrustResultCodeChangeTrustMalformed:
			return "op_malformed", nil
		case xdr.ChangeTrustResultCodeChangeTrustNoIssuer:
			return "op_no_issuer", nil
		case xdr.ChangeTrustResultCodeChangeTrustInvalidLimit:
			return "op_invalid_limit", nil
		case xdr.ChangeTrustResultCodeChangeTrustLowReserve:
			return "op_low_reserve", nil
		}
	case xdr.AllowTrustResultCode:
		switch code {
		case xdr.AllowTrustResultCodeAllowTrustSuccess:
			return "op_success", nil
		case xdr.AllowTrustResultCodeAllowTrustMalformed:
			return "op_malformed", nil
		case xdr.AllowTrustResultCodeAllowTrustNoTrustLine:
			return "op_no_trustline", nil
		case xdr.AllowTrustResultCodeAllowTrustTrustNotRequired:
			return "op_not_required", nil
		case xdr.AllowTrustResultCodeAllowTrustCantRevoke:
			return "op_cant_revoke", nil
		}
	case xdr.AccountMergeResultCode:
		switch code {
		case xdr.AccountMergeResultCodeAccountMergeSuccess:
			return "op_success", nil
		case xdr.AccountMergeResultCodeAccountMergeMalformed:
			return "op_malformed", nil
		case xdr.AccountMergeResultCodeAccountMergeNoAccount:
			return "op_no_account", nil
		case xdr.AccountMergeResultCodeAccountMergeImmutableSet:
			return "op_immutable_set", nil
		case xdr.AccountMergeResultCodeAccountMergeHasSubEntries:
			return "op_has_sub_entries", nil
		}
	case xdr.InflationResultCode:
		switch code {
		case xdr.InflationResultCodeInflationSuccess:
			return "op_success", nil
		case xdr.InflationResultCodeInflationNotTime:
			return "op_not_time", nil
		}
	}

	return "", errors.New(ErrUnknownCode)
}

func ForOperationResult(opr xdr.OperationResult) (string, error) {
	if opr.Code != xdr.OperationResultCodeOpInner {
		return String(opr.Code)
	}

	ir := opr.MustTr()
	var ic interface{}

	switch ir.Type {
	case xdr.OperationTypeCreateAccount:
		ic = ir.MustCreateAccountResult().Code
	case xdr.OperationTypePayment:
		ic = ir.MustPaymentResult().Code
	case xdr.OperationTypePathPayment:
		ic = ir.MustPathPaymentResult().Code
	case xdr.OperationTypeManageOffer:
		ic = ir.MustManageOfferResult().Code
	case xdr.OperationTypeCreatePassiveOffer:
		ic = ir.MustCreatePassiveOfferResult().Code
	case xdr.OperationTypeSetOptions:
		ic = ir.MustSetOptionsResult().Code
	case xdr.OperationTypeChangeTrust:
		ic = ir.MustChangeTrustResult().Code
	case xdr.OperationTypeAllowTrust:
		ic = ir.MustAllowTrustResult().Code
	case xdr.OperationTypeAccountMerge:
		ic = ir.MustAccountMergeResult().Code
	case xdr.OperationTypeInflation:
		ic = ir.MustInflationResult().Code
	}

	return String(ic)
}
<commit_msg>Delint codes package<commit_after>//Package codes is a helper package to help convert to transaction and operation result codes
//to strings used in horizon.
package codes

import (
	"github.com/go-errors/errors"
	"github.com/stellar/go-stellar-base/xdr"
)

// ErrUnknownCode is returned when an unexepcted value is provided to `String`
var ErrUnknownCode = errors.New("Unknown result code")

const (
	// OpSuccess is the string code used to specify the operation was successful
	OpSuccess = "op_success"
	// OpMalformed is the string code used to specify the operation was malformed
	// in some way.
	OpMalformed = "op_malformed"
	// OpUnderfunded is the string code used to specify the operation failed
	// due to a lack of funds.
	OpUnderfunded = "op_underfunded"

	// OpLowReserve is the string code used to specify the operation failed
	// because the account in question does not have enough balance to satisfy
	// what their new minimum balance would be.
	OpLowReserve = "op_low_reserve"

	// OpLineFull occurs when a payment would cause a destination account to
	// exceed their declared trust limit for the asset being sent.
	OpLineFull = "op_line_full"

	// OpNoIssuer occurs when a operation does not correctly specify an issuing
	// asset
	OpNoIssuer = "op_no_issuer"
)

//String returns the appropriate string representation of the provided result code
func String(code interface{}) (string, error) {
	switch code := code.(type) {
	case xdr.TransactionResultCode:
		switch code {
		case xdr.TransactionResultCodeTxSuccess:
			return "tx_success", nil
		case xdr.TransactionResultCodeTxFailed:
			return "tx_failed", nil
		case xdr.TransactionResultCodeTxTooEarly:
			return "tx_too_early", nil
		case xdr.TransactionResultCodeTxTooLate:
			return "tx_too_late", nil
		case xdr.TransactionResultCodeTxMissingOperation:
			return "tx_missing_operation", nil
		case xdr.TransactionResultCodeTxBadSeq:
			return "tx_bad_seq", nil
		case xdr.TransactionResultCodeTxBadAuth:
			return "tx_bad_auth", nil
		case xdr.TransactionResultCodeTxInsufficientBalance:
			return "tx_insufficient_balance", nil
		case xdr.TransactionResultCodeTxNoAccount:
			return "tx_no_source_account", nil
		case xdr.TransactionResultCodeTxInsufficientFee:
			return "tx_insufficient_fee", nil
		case xdr.TransactionResultCodeTxBadAuthExtra:
			return "tx_bad_auth_extra", nil
		case xdr.TransactionResultCodeTxInternalError:
			return "tx_internal_error", nil
		}
	case xdr.OperationResultCode:
		switch code {
		case xdr.OperationResultCodeOpInner:
			return "op_inner", nil
		case xdr.OperationResultCodeOpBadAuth:
			return "op_bad_auth", nil
		case xdr.OperationResultCodeOpNoAccount:
			return "op_no_source_account", nil
		}
	case xdr.CreateAccountResultCode:
		switch code {
		case xdr.CreateAccountResultCodeCreateAccountSuccess:
			return OpSuccess, nil
		case xdr.CreateAccountResultCodeCreateAccountMalformed:
			return OpMalformed, nil
		case xdr.CreateAccountResultCodeCreateAccountUnderfunded:
			return OpUnderfunded, nil
		case xdr.CreateAccountResultCodeCreateAccountLowReserve:
			return OpLowReserve, nil
		case xdr.CreateAccountResultCodeCreateAccountAlreadyExist:
			return "op_already_exists", nil
		}
	case xdr.PaymentResultCode:
		switch code {
		case xdr.PaymentResultCodePaymentSuccess:
			return OpSuccess, nil
		case xdr.PaymentResultCodePaymentMalformed:
			return OpMalformed, nil
		case xdr.PaymentResultCodePaymentUnderfunded:
			return OpUnderfunded, nil
		case xdr.PaymentResultCodePaymentSrcNoTrust:
			return "op_src_no_trust", nil
		case xdr.PaymentResultCodePaymentSrcNotAuthorized:
			return "op_src_not_authorized", nil
		case xdr.PaymentResultCodePaymentNoDestination:
			return "op_no_destination", nil
		case xdr.PaymentResultCodePaymentNoTrust:
			return "op_no_trust", nil
		case xdr.PaymentResultCodePaymentNotAuthorized:
			return "op_not_authorized", nil
		case xdr.PaymentResultCodePaymentLineFull:
			return OpLineFull, nil
		case xdr.PaymentResultCodePaymentNoIssuer:
			return OpNoIssuer, nil
		}
	case xdr.PathPaymentResultCode:
		switch code {
		case xdr.PathPaymentResultCodePathPaymentSuccess:
			return OpSuccess, nil
		case xdr.PathPaymentResultCodePathPaymentMalformed:
			return OpMalformed, nil
		case xdr.PathPaymentResultCodePathPaymentUnderfunded:
			return OpUnderfunded, nil
		case xdr.PathPaymentResultCodePathPaymentSrcNoTrust:
			return "op_src_no_trust", nil
		case xdr.PathPaymentResultCodePathPaymentSrcNotAuthorized:
			return "op_src_not_authorized", nil
		case xdr.PathPaymentResultCodePathPaymentNoDestination:
			return "op_no_destination", nil
		case xdr.PathPaymentResultCodePathPaymentNoTrust:
			return "op_no_trust", nil
		case xdr.PathPaymentResultCodePathPaymentNotAuthorized:
			return "op_not_authorized", nil
		case xdr.PathPaymentResultCodePathPaymentLineFull:
			return OpLineFull, nil
		case xdr.PathPaymentResultCodePathPaymentNoIssuer:
			return OpNoIssuer, nil
		case xdr.PathPaymentResultCodePathPaymentTooFewOffers:
			return "op_too_few_offers", nil
		case xdr.PathPaymentResultCodePathPaymentOfferCrossSelf:
			return "op_cross_self", nil
		case xdr.PathPaymentResultCodePathPaymentOverSendmax:
			return "op_over_source_max", nil
		}
	case xdr.ManageOfferResultCode:
		switch code {
		case xdr.ManageOfferResultCodeManageOfferSuccess:
			return OpSuccess, nil
		case xdr.ManageOfferResultCodeManageOfferMalformed:
			return OpMalformed, nil
		case xdr.ManageOfferResultCodeManageOfferSellNoTrust:
			return "op_sell_no_trust", nil
		case xdr.ManageOfferResultCodeManageOfferBuyNoTrust:
			return "op_buy_no_trust", nil
		case xdr.ManageOfferResultCodeManageOfferSellNotAuthorized:
			return "sell_not_authorized", nil
		case xdr.ManageOfferResultCodeManageOfferBuyNotAuthorized:
			return "buy_not_authorized", nil
		case xdr.ManageOfferResultCodeManageOfferLineFull:
			return OpLineFull, nil
		case xdr.ManageOfferResultCodeManageOfferUnderfunded:
			return OpUnderfunded, nil
		case xdr.ManageOfferResultCodeManageOfferCrossSelf:
			return "op_cross_self", nil
		case xdr.ManageOfferResultCodeManageOfferSellNoIssuer:
			return "op_sell_no_issuer", nil
		case xdr.ManageOfferResultCodeManageOfferBuyNoIssuer:
			return "buy_no_issuer", nil
		case xdr.ManageOfferResultCodeManageOfferNotFound:
			return "op_offer_not_found", nil
		case xdr.ManageOfferResultCodeManageOfferLowReserve:
			return OpLowReserve, nil
		}
	case xdr.SetOptionsResultCode:
		switch code {
		case xdr.SetOptionsResultCodeSetOptionsSuccess:
			return OpSuccess, nil
		case xdr.SetOptionsResultCodeSetOptionsLowReserve:
			return OpLowReserve, nil
		case xdr.SetOptionsResultCodeSetOptionsTooManySigners:
			return "op_too_many_signers", nil
		case xdr.SetOptionsResultCodeSetOptionsBadFlags:
			return "op_bad_flags", nil
		case xdr.SetOptionsResultCodeSetOptionsInvalidInflation:
			return "op_invalid_inflation", nil
		case xdr.SetOptionsResultCodeSetOptionsCantChange:
			return "op_cant_change", nil
		case xdr.SetOptionsResultCodeSetOptionsUnknownFlag:
			return "op_unknown_flag", nil
		case xdr.SetOptionsResultCodeSetOptionsThresholdOutOfRange:
			return "op_threshold_out_of_range", nil
		case xdr.SetOptionsResultCodeSetOptionsBadSigner:
			return "op_bad_signer", nil
		case xdr.SetOptionsResultCodeSetOptionsInvalidHomeDomain:
			return "op_invalid_home_domain", nil
		}
	case xdr.ChangeTrustResultCode:
		switch code {
		case xdr.ChangeTrustResultCodeChangeTrustSuccess:
			return OpSuccess, nil
		case xdr.ChangeTrustResultCodeChangeTrustMalformed:
			return OpMalformed, nil
		case xdr.ChangeTrustResultCodeChangeTrustNoIssuer:
			return OpNoIssuer, nil
		case xdr.ChangeTrustResultCodeChangeTrustInvalidLimit:
			return "op_invalid_limit", nil
		case xdr.ChangeTrustResultCodeChangeTrustLowReserve:
			return OpLowReserve, nil
		}
	case xdr.AllowTrustResultCode:
		switch code {
		case xdr.AllowTrustResultCodeAllowTrustSuccess:
			return OpSuccess, nil
		case xdr.AllowTrustResultCodeAllowTrustMalformed:
			return OpMalformed, nil
		case xdr.AllowTrustResultCodeAllowTrustNoTrustLine:
			return "op_no_trustline", nil
		case xdr.AllowTrustResultCodeAllowTrustTrustNotRequired:
			return "op_not_required", nil
		case xdr.AllowTrustResultCodeAllowTrustCantRevoke:
			return "op_cant_revoke", nil
		}
	case xdr.AccountMergeResultCode:
		switch code {
		case xdr.AccountMergeResultCodeAccountMergeSuccess:
			return OpSuccess, nil
		case xdr.AccountMergeResultCodeAccountMergeMalformed:
			return OpMalformed, nil
		case xdr.AccountMergeResultCodeAccountMergeNoAccount:
			return "op_no_account", nil
		case xdr.AccountMergeResultCodeAccountMergeImmutableSet:
			return "op_immutable_set", nil
		case xdr.AccountMergeResultCodeAccountMergeHasSubEntries:
			return "op_has_sub_entries", nil
		}
	case xdr.InflationResultCode:
		switch code {
		case xdr.InflationResultCodeInflationSuccess:
			return OpSuccess, nil
		case xdr.InflationResultCodeInflationNotTime:
			return "op_not_time", nil
		}
	}

	return "", errors.New(ErrUnknownCode)
}

// ForOperationResult returns the strong represtation used by horizon for the
// error code `opr`
func ForOperationResult(opr xdr.OperationResult) (string, error) {
	if opr.Code != xdr.OperationResultCodeOpInner {
		return String(opr.Code)
	}

	ir := opr.MustTr()
	var ic interface{}

	switch ir.Type {
	case xdr.OperationTypeCreateAccount:
		ic = ir.MustCreateAccountResult().Code
	case xdr.OperationTypePayment:
		ic = ir.MustPaymentResult().Code
	case xdr.OperationTypePathPayment:
		ic = ir.MustPathPaymentResult().Code
	case xdr.OperationTypeManageOffer:
		ic = ir.MustManageOfferResult().Code
	case xdr.OperationTypeCreatePassiveOffer:
		ic = ir.MustCreatePassiveOfferResult().Code
	case xdr.OperationTypeSetOptions:
		ic = ir.MustSetOptionsResult().Code
	case xdr.OperationTypeChangeTrust:
		ic = ir.MustChangeTrustResult().Code
	case xdr.OperationTypeAllowTrust:
		ic = ir.MustAllowTrustResult().Code
	case xdr.OperationTypeAccountMerge:
		ic = ir.MustAccountMergeResult().Code
	case xdr.OperationTypeInflation:
		ic = ir.MustInflationResult().Code
	}

	return String(ic)
}
<|endoftext|>


<commit_before>package dpn

/*
restobjects.go includes a number of structures used in communicating
with the DPN REST service. More info about the DPN REST service is
available at:

https://github.com/dpn-admin/DPN-REST-Wiki
https://github.com/dpn-admin/DPN-REST
*/

import (
	"time"
)

type DPNNode struct {
	Name                 string       `json:"name"`
	Namespace            string       `json:"namespace"`
	APIRoot              string       `json:"api_root"`
	SSHPubKey            string       `json:"ssh_pubkey"`
	ReplicateFrom        []string     `json:"replicate_from"`
	ReplicateTo          []string     `json:"replicate_to"`
	RestoreFrom          []string     `json:"restore_from"`
	RestoreTo            []string     `json:"restore_to"`
	Protocols            []string     `json:"protocols"`
	FixityAlgorithms     []string     `json:"fixity_algorithms"`
	CreatedAt            time.Time    `json:"created_at"`
	UpdatedAt            time.Time    `json:"updated_at"`
	Storage              *DPNStorage  `json:"storage"`
}

type DPNStorage struct {
	Region               string        `json:"region"`
	Type                 string        `json:"type"`
}

// DPNFixity represents a checksum for a bag in the DPN REST
// service.
type DPNFixity struct {

	// The algorithm used to check the fixity. Usually 'sha256',
	// but others may be valid in the future.
	Sha256               string       `json:"sha256"`

}

// DPNBag represents a Bag object in the DPN REST service.
// Like all of the DPN REST objects, it contains metadata only.
type DPNBag struct {

	// UUID is the unique identifier for a bag
	UUID               string               `json:"uuid"`

	// LocalId is the depositor's local identifier for a bag.
	LocalId            string               `json:"local_id"`

	// Size is the size, in bytes of the bag.
	Size               uint64               `json:"size"`

	// FirstVersionUUID is the UUID of the first version
	// of this bag.
	FirstVersionUUID   string               `json:"first_version_uuid"`

	// Version is the version or revision number of the bag. Starts at 1.
	Version            uint32               `json:"version"`

	// IngestNode is the node that first ingested or produced the bag.
	IngestNode       string                 `json:"ingest_node"`

	// AdminNode is the authoritative node for this bag. If various nodes
	// have conflicting registry info for this bag, the admin node wins.
	// The admin node also has some authority in restoring and (if its ever
	// possible) deleting bags.
	AdminNode          string               `json:"admin_node"`

	// BagType is one of 'D' (Data), 'R' (Rights) or 'I' (Interpretive)
	BagType            string               `json:"bag_type"`

	// Rights is a list of UUIDs of rights objects for this bag.
	Rights             []string             `json:"rights"`

	// Interpretive is a list of UUIDs of interpretive objects for this bag.
	Interpretive       []string             `json:"interpretive"`

	// ReplicatingNodes is a list of one more nodes that has stored
	// copies of this bag. The items in the list are node namespaces,
	// which are strings. E.g. ['aptrust', 'chron', 'tdr']
	ReplicatingNodes   []string             `json:"replicating_nodes"`

	// Fixities are the checksum/fixity values for this bag.
	Fixities           []*DPNFixity         `json:"fixities"`

	// CreatedAt is when this record was created.
	CreatedAt          time.Time            `json:"created_at"`

	// UpdatedAt is when this record was last updated.
	UpdatedAt          time.Time            `json:"updated_at"`
}

type DPNReplicationTransfer struct {

	// FromNode is the node where the bag is coming from.
	// The FromNode initiates the replication request.
	FromNode        string       `json:"from_node"`

	// ToNode is the node the bag is being transfered to
	ToNode          string       `json:"to_node"`

	// UUID is the UUID of the bag to be replicated
	UUID            string       `json:"uuid"`

	// ReplicationId is a unique id for this replication request.
	ReplicationId   string       `json:"replication_id"`

	// FixityAlgorithm is the algorithm used to calculate the fixity digest.
	FixityAlgorithm string       `json:"fixity_algorithm"`

	// FixityNonce is an optional nonce used to calculate the fixity digest.
	FixityNonce     string       `json:"fixity_nonce"`

	// FixityValue is the fixity value calculated by the ToNode after
	// it receives the bag. This will be null/empty until the replicating
	// node sends the info back to the FromNode.
	FixityValue     string       `json:"fixity_value"`

	// FixityAccept describes whether the FromNode accepts the fixity
	// value calculated by the ToNode. This is a nullable boolean,
	// so it has to be a pointer.
	FixityAccept    *bool        `json:"fixity_accept"`

	// BagValid is a value set by the ToNode to indicate whether
	// the bag it received was valid. This is a nullable boolean,
	// so it has to be a pointer.
	BagValid        *bool        `json:"bag_valid"`

	// Status is the status of the request, which can be any of:
	//
	// "Requested"  - The FromNode has requested this transfer.
	//                This means the transfer is new, and no
	//                action has been taken yet.
	// "Rejected"   - Set by the ToNode when it will not or cannot
	//                accept this transfer. (Usually due to disk space.)
	// "Received"   - Set by the ToNode to indicate it has received the
	//                the bag.
	// "Confirmed"  - Set by the FromNode after the bag has been confirmed
	//                valid, the fixity value has been approved, and the bag
	//                has been stored by the ToNode.
	// "Stored"     - Set by the ToNode after the bag has been copied to
	//                long-term storage.
	// "Cancelled"  - Can be set by either node for any reason. No further
	//                processing should occur on a cancelled request.
	Status          string       `json:"status"`

	// Protocol is the network protocol used to transfer the bag.
	// At launch, the only valid value for this is 'R' for rsync.
	Protocol        string       `json:"protocol"`

	// Link is a URL that the ToNode can use to copy the bag from the
	// FromNode. This value is set by the FromNode.
	Link            string       `json:"link"`

	// CreatedAt is the datetime when this record was created.
	CreatedAt       time.Time    `json:"created_at"`

	// UpdatedAt is the datetime when this record was last updated.
	UpdatedAt       time.Time    `json:"updated_at"`
}

type DPNRestoreTransfer struct {

	// RestoreId is a unique id for this restoration request.
	RestoreId       string       `json:"restore_id"`

	// FromNode is the node from which the bag should be restored.
	FromNode        string       `json:"from_node"`

	// ToNode is the node to which the bag should be restored.
	// The ToNode initiates a restoration request.
	ToNode          string       `json:"to_node"`

	// UUID is the unique identifier of the bag to be restored.
	UUID            string       `json:"uuid"`

	// Status is the status of the restoration operation. It can
	// have any of the following values:
	//
	// "Requested" - Default status used when record first created.
	// "Accepted"  - Indicates the FromNode has accepted the request to
	//               restore the bag.
	// "Rejected"  - Set by the FromNode if it cannot or will not restore
	//               the bag.
	// "Prepared"  - Set by the FromNode when the content has been restored
	//               locally and staged for transfer back to the to_node.
	// "Finished"  - Set by the ToNode after it has retrieved the restored
	//               bag from the FromNode.
	// "Cancelled" - Set by either node to indicate the restore operation
	//               was cancelled.
	Status          string       `json:"status"`

	// Protocol is the network protocol used to transfer the bag.
	// At launch, the only valid value for this is 'R' for rsync.
	Protocol        string       `json:"protocol"`

	// Link is a URL that the ToNode can use to copy the bag from the
	// FromNode. This value is set by the FromNode.
	Link            string       `json:"link"`

	// CreatedAt is the datetime when this record was created.
	CreatedAt       time.Time    `json:"created_at"`

	// UpdatedAt is the datetime when this record was last updated.
	UpdatedAt       time.Time    `json:"updated_at"`
}
<commit_msg>Added Node.LastPullDate<commit_after>package dpn

/*
restobjects.go includes a number of structures used in communicating
with the DPN REST service. More info about the DPN REST service is
available at:

https://github.com/dpn-admin/DPN-REST-Wiki
https://github.com/dpn-admin/DPN-REST
*/

import (
	"time"
)

type DPNNode struct {
	Name                 string       `json:"name"`
	Namespace            string       `json:"namespace"`
	APIRoot              string       `json:"api_root"`
	SSHPubKey            string       `json:"ssh_pubkey"`
	ReplicateFrom        []string     `json:"replicate_from"`
	ReplicateTo          []string     `json:"replicate_to"`
	RestoreFrom          []string     `json:"restore_from"`
	RestoreTo            []string     `json:"restore_to"`
	Protocols            []string     `json:"protocols"`
	FixityAlgorithms     []string     `json:"fixity_algorithms"`
	CreatedAt            time.Time    `json:"created_at"`
	UpdatedAt            time.Time    `json:"updated_at"`
	LastPullDate         time.Time    `json:"last_pull_date"`
	Storage              *DPNStorage  `json:"storage"`
}

type DPNStorage struct {
	Region               string        `json:"region"`
	Type                 string        `json:"type"`
}

// DPNFixity represents a checksum for a bag in the DPN REST
// service.
type DPNFixity struct {

	// The algorithm used to check the fixity. Usually 'sha256',
	// but others may be valid in the future.
	Sha256               string       `json:"sha256"`

}

// DPNBag represents a Bag object in the DPN REST service.
// Like all of the DPN REST objects, it contains metadata only.
type DPNBag struct {

	// UUID is the unique identifier for a bag
	UUID               string               `json:"uuid"`

	// LocalId is the depositor's local identifier for a bag.
	LocalId            string               `json:"local_id"`

	// Size is the size, in bytes of the bag.
	Size               uint64               `json:"size"`

	// FirstVersionUUID is the UUID of the first version
	// of this bag.
	FirstVersionUUID   string               `json:"first_version_uuid"`

	// Version is the version or revision number of the bag. Starts at 1.
	Version            uint32               `json:"version"`

	// IngestNode is the node that first ingested or produced the bag.
	IngestNode       string                 `json:"ingest_node"`

	// AdminNode is the authoritative node for this bag. If various nodes
	// have conflicting registry info for this bag, the admin node wins.
	// The admin node also has some authority in restoring and (if its ever
	// possible) deleting bags.
	AdminNode          string               `json:"admin_node"`

	// BagType is one of 'D' (Data), 'R' (Rights) or 'I' (Interpretive)
	BagType            string               `json:"bag_type"`

	// Rights is a list of UUIDs of rights objects for this bag.
	Rights             []string             `json:"rights"`

	// Interpretive is a list of UUIDs of interpretive objects for this bag.
	Interpretive       []string             `json:"interpretive"`

	// ReplicatingNodes is a list of one more nodes that has stored
	// copies of this bag. The items in the list are node namespaces,
	// which are strings. E.g. ['aptrust', 'chron', 'tdr']
	ReplicatingNodes   []string             `json:"replicating_nodes"`

	// Fixities are the checksum/fixity values for this bag.
	Fixities           []*DPNFixity         `json:"fixities"`

	// CreatedAt is when this record was created.
	CreatedAt          time.Time            `json:"created_at"`

	// UpdatedAt is when this record was last updated.
	UpdatedAt          time.Time            `json:"updated_at"`
}

type DPNReplicationTransfer struct {

	// FromNode is the node where the bag is coming from.
	// The FromNode initiates the replication request.
	FromNode        string       `json:"from_node"`

	// ToNode is the node the bag is being transfered to
	ToNode          string       `json:"to_node"`

	// UUID is the UUID of the bag to be replicated
	UUID            string       `json:"uuid"`

	// ReplicationId is a unique id for this replication request.
	ReplicationId   string       `json:"replication_id"`

	// FixityAlgorithm is the algorithm used to calculate the fixity digest.
	FixityAlgorithm string       `json:"fixity_algorithm"`

	// FixityNonce is an optional nonce used to calculate the fixity digest.
	FixityNonce     string       `json:"fixity_nonce"`

	// FixityValue is the fixity value calculated by the ToNode after
	// it receives the bag. This will be null/empty until the replicating
	// node sends the info back to the FromNode.
	FixityValue     string       `json:"fixity_value"`

	// FixityAccept describes whether the FromNode accepts the fixity
	// value calculated by the ToNode. This is a nullable boolean,
	// so it has to be a pointer.
	FixityAccept    *bool        `json:"fixity_accept"`

	// BagValid is a value set by the ToNode to indicate whether
	// the bag it received was valid. This is a nullable boolean,
	// so it has to be a pointer.
	BagValid        *bool        `json:"bag_valid"`

	// Status is the status of the request, which can be any of:
	//
	// "Requested"  - The FromNode has requested this transfer.
	//                This means the transfer is new, and no
	//                action has been taken yet.
	// "Rejected"   - Set by the ToNode when it will not or cannot
	//                accept this transfer. (Usually due to disk space.)
	// "Received"   - Set by the ToNode to indicate it has received the
	//                the bag.
	// "Confirmed"  - Set by the FromNode after the bag has been confirmed
	//                valid, the fixity value has been approved, and the bag
	//                has been stored by the ToNode.
	// "Stored"     - Set by the ToNode after the bag has been copied to
	//                long-term storage.
	// "Cancelled"  - Can be set by either node for any reason. No further
	//                processing should occur on a cancelled request.
	Status          string       `json:"status"`

	// Protocol is the network protocol used to transfer the bag.
	// At launch, the only valid value for this is 'R' for rsync.
	Protocol        string       `json:"protocol"`

	// Link is a URL that the ToNode can use to copy the bag from the
	// FromNode. This value is set by the FromNode.
	Link            string       `json:"link"`

	// CreatedAt is the datetime when this record was created.
	CreatedAt       time.Time    `json:"created_at"`

	// UpdatedAt is the datetime when this record was last updated.
	UpdatedAt       time.Time    `json:"updated_at"`
}

type DPNRestoreTransfer struct {

	// RestoreId is a unique id for this restoration request.
	RestoreId       string       `json:"restore_id"`

	// FromNode is the node from which the bag should be restored.
	FromNode        string       `json:"from_node"`

	// ToNode is the node to which the bag should be restored.
	// The ToNode initiates a restoration request.
	ToNode          string       `json:"to_node"`

	// UUID is the unique identifier of the bag to be restored.
	UUID            string       `json:"uuid"`

	// Status is the status of the restoration operation. It can
	// have any of the following values:
	//
	// "Requested" - Default status used when record first created.
	// "Accepted"  - Indicates the FromNode has accepted the request to
	//               restore the bag.
	// "Rejected"  - Set by the FromNode if it cannot or will not restore
	//               the bag.
	// "Prepared"  - Set by the FromNode when the content has been restored
	//               locally and staged for transfer back to the to_node.
	// "Finished"  - Set by the ToNode after it has retrieved the restored
	//               bag from the FromNode.
	// "Cancelled" - Set by either node to indicate the restore operation
	//               was cancelled.
	Status          string       `json:"status"`

	// Protocol is the network protocol used to transfer the bag.
	// At launch, the only valid value for this is 'R' for rsync.
	Protocol        string       `json:"protocol"`

	// Link is a URL that the ToNode can use to copy the bag from the
	// FromNode. This value is set by the FromNode.
	Link            string       `json:"link"`

	// CreatedAt is the datetime when this record was created.
	CreatedAt       time.Time    `json:"created_at"`

	// UpdatedAt is the datetime when this record was last updated.
	UpdatedAt       time.Time    `json:"updated_at"`
}
<|endoftext|>


<commit_before>package reports

import (
	"encoding/json"
	"time"
)

type Report struct {
	ID            string            `json:"id"`
	TeamID        string            `json:"team_id"`
	ProjectID     string            `json:"project_id"`
	BuildNumber   string            `json:"build_number"`
	Name          string            `json:"name"`
	Text          string            `json:"text"`
	Type          string            `json:"type"`
	Source        string            `json:"source"`
	Branch        string            `json:"branch"`
	Description   string            `json:"description"`
	Status        string            `json:"status"`
	RulesetID     string            `json:"ruleset_id"`
	RulesetName   string            `json:"ruleset_name"`
	Passed        bool              `json:"passed"`
	CreatedAt     time.Time         `json:"created_at"`
	UpdatedAt     time.Time         `json:"updated_at"`
	Duration      float64           `json:"duration"`
	Trigger       string            `json:"trigger"`
	TriggerHash   string            `json:"trigger_hash"`
	TriggerText   string            `json:"trigger_text"`
	TriggerAuthor string            `json:"trigger_author"`
	Risk          string            `json:"risk"`
	Summary       string            `json:"summary"`
	ScanSummaries []json.RawMessage `json:"scan_summaries"`
}
<commit_msg>adding xml tags to report<commit_after>package reports

import (
	"encoding/json"
	"time"
)

// Report is a Ion Channel representation of a report output from a given analysis
type Report struct {
	ID            string            `json:"id" xml:"id"`
	TeamID        string            `json:"team_id" xml:"team_id"`
	ProjectID     string            `json:"project_id" xml:"project_id"`
	BuildNumber   string            `json:"build_number" xml:"build_number"`
	Name          string            `json:"name" xml:"name"`
	Text          string            `json:"text" xml:"text"`
	Type          string            `json:"type" xml:"type"`
	Source        string            `json:"source" xml:"source"`
	Branch        string            `json:"branch" xml:"branch"`
	Description   string            `json:"description" xml:"description"`
	Status        string            `json:"status" xml:"status"`
	RulesetID     string            `json:"ruleset_id" xml:"ruleset_id"`
	RulesetName   string            `json:"ruleset_name" xml:"ruleset_name"`
	Passed        bool              `json:"passed" xml:"passed"`
	CreatedAt     time.Time         `json:"created_at" xml:"created_at"`
	UpdatedAt     time.Time         `json:"updated_at" xml:"updated_at"`
	Duration      float64           `json:"duration" xml:"duration"`
	Trigger       string            `json:"trigger" xml:"trigger"`
	TriggerHash   string            `json:"trigger_hash" xml:"trigger_hash"`
	TriggerText   string            `json:"trigger_text" xml:"trigger_text"`
	TriggerAuthor string            `json:"trigger_author" xml:"trigger_author"`
	Risk          string            `json:"risk" xml:"risk"`
	Summary       string            `json:"summary" xml:"summary"`
	ScanSummaries []json.RawMessage `json:"scan_summaries" xml:"scan_summaries"`
}
<|endoftext|>


<commit_before>package drivers

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/xo/dburl"
	"github.com/xo/usql/stmt"
	"github.com/xo/usql/text"
)

// DB is the common interface for database operations, compatible with
// database/sql.DB and database/sql.Tx.
type DB interface {
	Exec(string, ...interface{}) (sql.Result, error)
	Query(string, ...interface{}) (*sql.Rows, error)
	QueryRow(string, ...interface{}) *sql.Row
	Prepare(string) (*sql.Stmt, error)
}

// Driver holds funcs for a driver.
type Driver struct {
	// N is a name to override the driver name with.
	N string

	// AD will be passed to query buffers to enable dollar ($$) style strings.
	AD bool

	// AMC will be passed to query buffers to enable multiline (/**/) style
	// comments.
	AMC bool

	// ACC will be passed to query buffers to enable C (//) style comments.
	ACC bool

	// AHC will be passed to query buffers to enable hash (#) style comments.
	AHC bool

	// ReqPP will be used by RequirePreviousPassword.
	ReqPP bool

	// O will be used by Open if defined.
	O func(*dburl.URL) (func(string, string) (*sql.DB, error), error)

	// V will be used by Version if defined.
	V func(DB) (string, error)

	// U will be used by User if defined.
	U func(DB) (string, error)

	// ChPw will be used by ChangePassword if defined.
	ChPw func(DB, string, string, string) error

	// PwErr will be used by IsPasswordErr if defined.
	PwErr func(error) bool

	// P will be used by Process if defined.
	P func(string, string) (string, string, bool, error)

	// Cols will be used to retrieve the columns for the rows if defined.
	Cols func(*sql.Rows) ([]string, error)

	// Cb will be used by ConvertBytes to convert a raw []byte slice to a
	// string if defined.
	Cb func([]byte) string

	// E will be used by Error.Error if defined.
	E func(error) (string, string)

	// EV will be used by Error.Verbose if defined.
	EV func(error) *ErrVerbose

	// A will be used by RowsAffected if defined.
	A func(sql.Result) (int64, error)
}

// drivers is the map of drivers funcs.
var drivers map[string]Driver

func init() {
	drivers = make(map[string]Driver)
}

// Available returns the available drivers.
func Available() map[string]Driver {
	return drivers
}

// Register registers driver d with name and associated aliases.
func Register(name string, d Driver, aliases ...string) {
	if _, ok := drivers[name]; ok {
		panic(fmt.Sprintf("driver %s is already registered", name))
	}

	drivers[name] = d

	for _, alias := range aliases {
		if _, ok := drivers[alias]; ok {
			panic(fmt.Sprintf("alias %s is already registered", name))
		}

		drivers[alias] = d
	}
}

// Registered returns whether or not a specific driver has been registered.
func Registered(name string) bool {
	_, ok := drivers[name]
	return ok
}

// Open opens a sql.DB connection for the registered driver.
func Open(u *dburl.URL, buf *stmt.Stmt) (*sql.DB, error) {
	var err error

	d, ok := drivers[u.Driver]
	if !ok {
		return nil, WrapErr(u.Driver, text.ErrDriverNotAvailable)
	}

	// force query buffer settings
	stmt.AllowDollar(d.AD)(buf)
	stmt.AllowMultilineComments(d.AMC)(buf)

	f := sql.Open
	if d.O != nil {
		f, err = d.O(u)
		if err != nil {
			return nil, WrapErr(u.Driver, err)
		}
	}

	db, err := f(u.Driver, u.DSN)
	if err != nil {
		return nil, WrapErr(u.Driver, err)
	}

	return db, nil
}

// Version returns information about the database connection for the specified
// URL's driver.
func Version(u *dburl.URL, db DB) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.V != nil {
		ver, err := d.V(db)
		return ver, WrapErr(u.Driver, err)
	}

	var ver string
	err := db.QueryRow(`select version();`).Scan(&ver)
	if err != nil || ver == "" {
		ver = "<unknown>"
	}
	return ver, nil
}

// User returns the current database user for the specified URL's driver.
func User(u *dburl.URL, db DB) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.U != nil {
		user, err := d.U(db)
		return user, WrapErr(u.Driver, err)
	}

	var user string
	db.QueryRow(`select current_user`).Scan(&user)
	return user, nil
}

// Process processes the supplied SQL query for the specified URL's driver.
func Process(u *dburl.URL, prefix, sqlstr string) (string, string, bool, error) {
	if d, ok := drivers[u.Driver]; ok && d.P != nil {
		a, b, c, err := d.P(prefix, sqlstr)
		return a, b, c, WrapErr(u.Driver, err)
	}

	typ, q := QueryExecType(prefix, sqlstr)
	return typ, sqlstr, q, nil
}

// IsPasswordErr returns true if the specified err is a password error for the
// specified URL's driver.
func IsPasswordErr(u *dburl.URL, err error) bool {
	drv := u.Driver
	if e, ok := err.(*Error); ok {
		drv, err = e.Driver, e.Err
	}

	if d, ok := drivers[drv]; ok && d.PwErr != nil {
		return d.PwErr(err)
	}
	return false
}

// RequirePreviousPassword returns true if the specified URL's driver requires
// a previous password when changing a user's password.
func RequirePreviousPassword(u *dburl.URL) bool {
	if d, ok := drivers[u.Driver]; ok {
		return d.ReqPP
	}
	return false
}

// CanChangePassword returns whether or not the specified driver's URL supports
// changing passwords.
func CanChangePassword(u *dburl.URL) error {
	if d, ok := drivers[u.Driver]; ok && d.ChPw != nil {
		return nil
	}
	return text.ErrPasswordNotSupportedByDriver
}

// ChangePassword initiates a user password change for the specified URL's
// driver. If user is not supplied, then the current user will be retrieved
// from User.
func ChangePassword(u *dburl.URL, db DB, user, new, old string) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ChPw != nil {
		var err error
		if user == "" {
			user, err = User(u, db)
			if err != nil {
				return "", err
			}
		}

		return user, d.ChPw(db, user, new, old)
	}
	return "", text.ErrPasswordNotSupportedByDriver
}

// Columns returns the columns for SQL result for the specified URL's driver.
func Columns(u *dburl.URL, rows *sql.Rows) ([]string, error) {
	var cols []string
	var err error

	if d, ok := drivers[u.Driver]; ok && d.Cols != nil {
		cols, err = d.Cols(rows)
	} else {
		cols, err = rows.Columns()
	}

	if err != nil {
		return nil, WrapErr(u.Driver, err)
	}

	for i, c := range cols {
		if strings.TrimSpace(c) == "" {
			cols[i] = fmt.Sprintf("col%d", i)
		}
	}

	return cols, nil
}

// ConvertBytes converts a raw byte slice for a specified URL's driver.
func ConvertBytes(u *dburl.URL, buf []byte) string {
	if d, ok := drivers[u.Driver]; ok && d.Cb != nil {
		return d.Cb(buf)
	}
	return string(buf)
}

// RowsAffected returns the rows affected for the SQL result for a specified
// URL's driver.
func RowsAffected(u *dburl.URL, res sql.Result) (int64, error) {
	var count int64
	var err error
	if d, ok := drivers[u.Driver]; ok && d.A != nil {
		count, err = d.A(res)
	} else {
		count, err = res.RowsAffected()
	}
	if err != nil {
		return 0, WrapErr(u.Driver, err)
	}

	return count, nil
}

// Ping pings the database for a specified URL's driver.
func Ping(u *dburl.URL, db *sql.DB) error {
	return WrapErr(u.Driver, db.Ping())
}
<commit_msg>Fixing missing driver settings on new connection<commit_after>package drivers

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/xo/dburl"
	"github.com/xo/usql/stmt"
	"github.com/xo/usql/text"
)

// DB is the common interface for database operations, compatible with
// database/sql.DB and database/sql.Tx.
type DB interface {
	Exec(string, ...interface{}) (sql.Result, error)
	Query(string, ...interface{}) (*sql.Rows, error)
	QueryRow(string, ...interface{}) *sql.Row
	Prepare(string) (*sql.Stmt, error)
}

// Driver holds funcs for a driver.
type Driver struct {
	// N is a name to override the driver name with.
	N string

	// AD will be passed to query buffers to enable dollar ($$) style strings.
	AD bool

	// AMC will be passed to query buffers to enable multiline (/**/) style
	// comments.
	AMC bool

	// ACC will be passed to query buffers to enable C (//) style comments.
	ACC bool

	// AHC will be passed to query buffers to enable hash (#) style comments.
	AHC bool

	// ReqPP will be used by RequirePreviousPassword.
	ReqPP bool

	// O will be used by Open if defined.
	O func(*dburl.URL) (func(string, string) (*sql.DB, error), error)

	// V will be used by Version if defined.
	V func(DB) (string, error)

	// U will be used by User if defined.
	U func(DB) (string, error)

	// ChPw will be used by ChangePassword if defined.
	ChPw func(DB, string, string, string) error

	// PwErr will be used by IsPasswordErr if defined.
	PwErr func(error) bool

	// P will be used by Process if defined.
	P func(string, string) (string, string, bool, error)

	// Cols will be used to retrieve the columns for the rows if defined.
	Cols func(*sql.Rows) ([]string, error)

	// Cb will be used by ConvertBytes to convert a raw []byte slice to a
	// string if defined.
	Cb func([]byte) string

	// E will be used by Error.Error if defined.
	E func(error) (string, string)

	// EV will be used by Error.Verbose if defined.
	EV func(error) *ErrVerbose

	// A will be used by RowsAffected if defined.
	A func(sql.Result) (int64, error)
}

// drivers is the map of drivers funcs.
var drivers map[string]Driver

func init() {
	drivers = make(map[string]Driver)
}

// Available returns the available drivers.
func Available() map[string]Driver {
	return drivers
}

// Register registers driver d with name and associated aliases.
func Register(name string, d Driver, aliases ...string) {
	if _, ok := drivers[name]; ok {
		panic(fmt.Sprintf("driver %s is already registered", name))
	}

	drivers[name] = d

	for _, alias := range aliases {
		if _, ok := drivers[alias]; ok {
			panic(fmt.Sprintf("alias %s is already registered", name))
		}

		drivers[alias] = d
	}
}

// Registered returns whether or not a specific driver has been registered.
func Registered(name string) bool {
	_, ok := drivers[name]
	return ok
}

// Open opens a sql.DB connection for the registered driver.
func Open(u *dburl.URL, buf *stmt.Stmt) (*sql.DB, error) {
	var err error

	d, ok := drivers[u.Driver]
	if !ok {
		return nil, WrapErr(u.Driver, text.ErrDriverNotAvailable)
	}

	// force query buffer settings
	stmt.AllowDollar(d.AD)(buf)
	stmt.AllowMultilineComments(d.AMC)(buf)
	stmt.AllowCComments(d.ACC)(buf)
	stmt.AllowHashComments(d.AHC)(buf)

	f := sql.Open
	if d.O != nil {
		f, err = d.O(u)
		if err != nil {
			return nil, WrapErr(u.Driver, err)
		}
	}

	db, err := f(u.Driver, u.DSN)
	if err != nil {
		return nil, WrapErr(u.Driver, err)
	}

	return db, nil
}

// Version returns information about the database connection for the specified
// URL's driver.
func Version(u *dburl.URL, db DB) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.V != nil {
		ver, err := d.V(db)
		return ver, WrapErr(u.Driver, err)
	}

	var ver string
	err := db.QueryRow(`select version();`).Scan(&ver)
	if err != nil || ver == "" {
		ver = "<unknown>"
	}
	return ver, nil
}

// User returns the current database user for the specified URL's driver.
func User(u *dburl.URL, db DB) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.U != nil {
		user, err := d.U(db)
		return user, WrapErr(u.Driver, err)
	}

	var user string
	db.QueryRow(`select current_user`).Scan(&user)
	return user, nil
}

// Process processes the supplied SQL query for the specified URL's driver.
func Process(u *dburl.URL, prefix, sqlstr string) (string, string, bool, error) {
	if d, ok := drivers[u.Driver]; ok && d.P != nil {
		a, b, c, err := d.P(prefix, sqlstr)
		return a, b, c, WrapErr(u.Driver, err)
	}

	typ, q := QueryExecType(prefix, sqlstr)
	return typ, sqlstr, q, nil
}

// IsPasswordErr returns true if the specified err is a password error for the
// specified URL's driver.
func IsPasswordErr(u *dburl.URL, err error) bool {
	drv := u.Driver
	if e, ok := err.(*Error); ok {
		drv, err = e.Driver, e.Err
	}

	if d, ok := drivers[drv]; ok && d.PwErr != nil {
		return d.PwErr(err)
	}
	return false
}

// RequirePreviousPassword returns true if the specified URL's driver requires
// a previous password when changing a user's password.
func RequirePreviousPassword(u *dburl.URL) bool {
	if d, ok := drivers[u.Driver]; ok {
		return d.ReqPP
	}
	return false
}

// CanChangePassword returns whether or not the specified driver's URL supports
// changing passwords.
func CanChangePassword(u *dburl.URL) error {
	if d, ok := drivers[u.Driver]; ok && d.ChPw != nil {
		return nil
	}
	return text.ErrPasswordNotSupportedByDriver
}

// ChangePassword initiates a user password change for the specified URL's
// driver. If user is not supplied, then the current user will be retrieved
// from User.
func ChangePassword(u *dburl.URL, db DB, user, new, old string) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ChPw != nil {
		var err error
		if user == "" {
			user, err = User(u, db)
			if err != nil {
				return "", err
			}
		}

		return user, d.ChPw(db, user, new, old)
	}
	return "", text.ErrPasswordNotSupportedByDriver
}

// Columns returns the columns for SQL result for the specified URL's driver.
func Columns(u *dburl.URL, rows *sql.Rows) ([]string, error) {
	var cols []string
	var err error

	if d, ok := drivers[u.Driver]; ok && d.Cols != nil {
		cols, err = d.Cols(rows)
	} else {
		cols, err = rows.Columns()
	}

	if err != nil {
		return nil, WrapErr(u.Driver, err)
	}

	for i, c := range cols {
		if strings.TrimSpace(c) == "" {
			cols[i] = fmt.Sprintf("col%d", i)
		}
	}

	return cols, nil
}

// ConvertBytes converts a raw byte slice for a specified URL's driver.
func ConvertBytes(u *dburl.URL, buf []byte) string {
	if d, ok := drivers[u.Driver]; ok && d.Cb != nil {
		return d.Cb(buf)
	}
	return string(buf)
}

// RowsAffected returns the rows affected for the SQL result for a specified
// URL's driver.
func RowsAffected(u *dburl.URL, res sql.Result) (int64, error) {
	var count int64
	var err error
	if d, ok := drivers[u.Driver]; ok && d.A != nil {
		count, err = d.A(res)
	} else {
		count, err = res.RowsAffected()
	}
	if err != nil {
		return 0, WrapErr(u.Driver, err)
	}

	return count, nil
}

// Ping pings the database for a specified URL's driver.
func Ping(u *dburl.URL, db *sql.DB) error {
	return WrapErr(u.Driver, db.Ping())
}
<|endoftext|>


<commit_before>package imagejob

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/trilopin/godinary/storage"

	bimg "gopkg.in/h2non/bimg.v1"
)

var (
	// MaxRequest is global max concurrency for download
	MaxRequest int
	// MaxRequestPerDomain is max concurrency per out domain
	MaxRequestPerDomain int
	// SpecificThrotling is semaphore per domain
	SpecificThrotling map[string]chan struct{}
	// GlobalThrotling is global semaphore
	GlobalThrotling chan struct{}
)

// Fetch takes url + params in url to download image from url and apply filters
func Fetch(w http.ResponseWriter, r *http.Request) {
	var body io.Reader
	var dSem float64

	t1 := time.Now()
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	urlInfo := strings.Replace(r.URL.Path, "/image/fetch/", "", 1)
	job := NewImageJob()
	job.AcceptWebp = strings.Contains(r.Header["Accept"][0], "image/webp")

	if err := job.Parse(urlInfo); err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	domain, err := topDomain(job.Source.URL)
	if err != nil || domain == "" {
		http.Error(w, "Cannot parse domain", http.StatusInternalServerError)
		return
	}
	domainThrotle := make(chan struct{}, MaxRequestPerDomain)
	SpecificThrotling[domain] = domainThrotle

	// derived image is already cached
	if body, err = storage.StorageDriver.Read(job.Target.Hash); err == nil {
		if cached, err2 := ioutil.ReadAll(body); err2 == nil {
			writeImage(w, cached, job.Target.Format)
			log.Printf("CACHED - TOTAL %0.5f", time.Since(t1).Seconds())
			return
		}
	}

	// Download if original image does not exists at storage, load otherwise
	body, err = storage.StorageDriver.Read(job.Source.Hash)
	if err == nil {
		job.Source.Load(body)
	} else {
		tSem := time.Now()
		log.Printf("SEM %s %d/%d %d/%d", domain, len(GlobalThrotling), cap(GlobalThrotling), len(domainThrotle), cap(domainThrotle))
		GlobalThrotling <- struct{}{}
		domainThrotle <- struct{}{}
		dSem = time.Since(tSem).Seconds()
		err = job.Source.Download(storage.StorageDriver)
		<-domainThrotle
		<-GlobalThrotling

		if err != nil {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
	}
	t2 := time.Now()

	job.Source.ExtractInfo()
	job.crop()

	// do the process thing
	if err := job.Target.Process(job.Source, storage.StorageDriver); err != nil {
		log.Println("Error processing image ", job.Source.URL, err)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
	}
	t3 := time.Now()

	writeImage(w, job.Target.RawContent, job.Target.Format)
	log.Printf(
		"NEW - TOTAL %0.5f => SEM %0.5f, DOWN %0.5f, PROC %0.5f",
		time.Since(t1).Seconds(), dSem,
		t2.Sub(t1).Seconds()-dSem, t3.Sub(t2).Seconds())

}

func topDomain(URL string) (string, error) {
	info, err := url.Parse(URL)
	if err != nil {
		return "", errors.New("Cannot parse hostname")
	}
	return info.Host, nil
}

func writeImage(w http.ResponseWriter, buffer []byte, format bimg.ImageType) {
	w.Header().Set("Content-Length", strconv.Itoa(len(buffer)))
	w.Header().Set("Content-Type", fmt.Sprintf("image/%s", bimg.ImageTypes[format]))
	w.Write(buffer)
}
<commit_msg>avoid concurrent map writes (crash app) (#10)<commit_after>package imagejob

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/trilopin/godinary/storage"

	bimg "gopkg.in/h2non/bimg.v1"
)

var (
	// MaxRequest is global max concurrency for download
	MaxRequest int
	// MaxRequestPerDomain is max concurrency per out domain
	MaxRequestPerDomain int
	// SpecificThrotling is semaphore per domain
	SpecificThrotling map[string]chan struct{}
	// GlobalThrotling is global semaphore
	GlobalThrotling chan struct{}
)

// Fetch takes url + params in url to download image from url and apply filters
func Fetch(w http.ResponseWriter, r *http.Request) {
	var body io.Reader
	var dSem float64

	t1 := time.Now()
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	urlInfo := strings.Replace(r.URL.Path, "/image/fetch/", "", 1)
	job := NewImageJob()
	job.AcceptWebp = strings.Contains(r.Header["Accept"][0], "image/webp")

	if err := job.Parse(urlInfo); err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	domain, err := topDomain(job.Source.URL)
	if err != nil || domain == "" {
		http.Error(w, "Cannot parse domain", http.StatusInternalServerError)
		return
	}
	if _, ok := SpecificThrotling[domain]; !ok {
		SpecificThrotling[domain] = make(chan struct{}, MaxRequestPerDomain)
	}

	// derived image is already cached
	if body, err = storage.StorageDriver.Read(job.Target.Hash); err == nil {
		if cached, err2 := ioutil.ReadAll(body); err2 == nil {
			writeImage(w, cached, job.Target.Format)
			log.Printf("CACHED - TOTAL %0.5f", time.Since(t1).Seconds())
			return
		}
	}

	// Download if original image does not exists at storage, load otherwise
	body, err = storage.StorageDriver.Read(job.Source.Hash)
	if err == nil {
		job.Source.Load(body)
	} else {
		tSem := time.Now()
		//log.Printf("SEM %s %d/%d %d/%d", domain, len(GlobalThrotling), cap(GlobalThrotling))
		log.Printf("SEM %s %d/%d %d/%d", domain, len(GlobalThrotling), cap(GlobalThrotling), len(SpecificThrotling[domain]), cap(SpecificThrotling[domain]))
		GlobalThrotling <- struct{}{}
		SpecificThrotling[domain] <- struct{}{}
		dSem = time.Since(tSem).Seconds()
		err = job.Source.Download(storage.StorageDriver)
		<-SpecificThrotling[domain]
		<-GlobalThrotling

		if err != nil {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
	}
	t2 := time.Now()

	job.Source.ExtractInfo()
	job.crop()

	// do the process thing
	if err := job.Target.Process(job.Source, storage.StorageDriver); err != nil {
		log.Println("Error processing image ", job.Source.URL, err)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
	}
	t3 := time.Now()

	writeImage(w, job.Target.RawContent, job.Target.Format)
	log.Printf(
		"NEW - TOTAL %0.5f => SEM %0.5f, DOWN %0.5f, PROC %0.5f",
		time.Since(t1).Seconds(), dSem,
		t2.Sub(t1).Seconds()-dSem, t3.Sub(t2).Seconds())

}

func topDomain(URL string) (string, error) {
	info, err := url.Parse(URL)
	if err != nil {
		return "", errors.New("Cannot parse hostname")
	}
	return info.Host, nil
}

func writeImage(w http.ResponseWriter, buffer []byte, format bimg.ImageType) {
	w.Header().Set("Content-Length", strconv.Itoa(len(buffer)))
	w.Header().Set("Content-Type", fmt.Sprintf("image/%s", bimg.ImageTypes[format]))
	w.Write(buffer)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package lifecycle

import (
	"context"
	"fmt"
	"io"
	"time"

	"k8s.io/klog/v2"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	utilcache "k8s.io/apimachinery/pkg/util/cache"
	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apiserver/pkg/admission"
	"k8s.io/apiserver/pkg/admission/initializer"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	corelisters "k8s.io/client-go/listers/core/v1"
)

const (
	// PluginName indicates the name of admission plug-in
	PluginName = "NamespaceLifecycle"
	// how long a namespace stays in the force live lookup cache before expiration.
	forceLiveLookupTTL = 30 * time.Second
	// how long to wait for a missing namespace before re-checking the cache (and then doing a live lookup)
	// this accomplishes two things:
	// 1. It allows a watch-fed cache time to observe a namespace creation event
	// 2. It allows time for a namespace creation to distribute to members of a storage cluster,
	//    so the live lookup has a better chance of succeeding even if it isn't performed against the leader.
	missingNamespaceWait = 50 * time.Millisecond
)

// Register registers a plugin
func Register(plugins *admission.Plugins) {
	plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
		return NewLifecycle(sets.NewString(metav1.NamespaceDefault, metav1.NamespaceSystem, metav1.NamespacePublic))
	})
}

// Lifecycle is an implementation of admission.Interface.
// It enforces life-cycle constraints around a Namespace depending on its Phase
type Lifecycle struct {
	*admission.Handler
	client             kubernetes.Interface
	immortalNamespaces sets.String
	namespaceLister    corelisters.NamespaceLister
	// forceLiveLookupCache holds a list of entries for namespaces that we have a strong reason to believe are stale in our local cache.
	// if a namespace is in this cache, then we will ignore our local state and always fetch latest from api server.
	forceLiveLookupCache *utilcache.LRUExpireCache
}

var _ = initializer.WantsExternalKubeInformerFactory(&Lifecycle{})
var _ = initializer.WantsExternalKubeClientSet(&Lifecycle{})

// Admit makes an admission decision based on the request attributes
func (l *Lifecycle) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
	// prevent deletion of immortal namespaces
	if a.GetOperation() == admission.Delete && a.GetKind().GroupKind() == v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() && l.immortalNamespaces.Has(a.GetName()) {
		return errors.NewForbidden(a.GetResource().GroupResource(), a.GetName(), fmt.Errorf("this namespace may not be deleted"))
	}

	// always allow non-namespaced resources
	if len(a.GetNamespace()) == 0 && a.GetKind().GroupKind() != v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() {
		return nil
	}

	if a.GetKind().GroupKind() == v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() {
		// if a namespace is deleted, we want to prevent all further creates into it
		// while it is undergoing termination.  to reduce incidences where the cache
		// is slow to update, we add the namespace into a force live lookup list to ensure
		// we are not looking at stale state.
		if a.GetOperation() == admission.Delete {
			l.forceLiveLookupCache.Add(a.GetName(), true, forceLiveLookupTTL)
		}
		// allow all operations to namespaces
		return nil
	}

	// always allow deletion of other resources
	if a.GetOperation() == admission.Delete {
		return nil
	}

	// always allow access review checks.  Returning status about the namespace would be leaking information
	if isAccessReview(a) {
		return nil
	}

	// we need to wait for our caches to warm
	if !l.WaitForReady() {
		return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request"))
	}

	var (
		exists bool
		err    error
	)

	namespace, err := l.namespaceLister.Get(a.GetNamespace())
	if err != nil {
		if !errors.IsNotFound(err) {
			return errors.NewInternalError(err)
		}
	} else {
		exists = true
	}

	if !exists && a.GetOperation() == admission.Create {
		// give the cache time to observe the namespace before rejecting a create.
		// this helps when creating a namespace and immediately creating objects within it.
		time.Sleep(missingNamespaceWait)
		namespace, err = l.namespaceLister.Get(a.GetNamespace())
		switch {
		case errors.IsNotFound(err):
			// no-op
		case err != nil:
			return errors.NewInternalError(err)
		default:
			exists = true
		}
		if exists {
			klog.V(4).Infof("found %s in cache after waiting", a.GetNamespace())
		}
	}

	// forceLiveLookup if true will skip looking at local cache state and instead always make a live call to server.
	forceLiveLookup := false
	if _, ok := l.forceLiveLookupCache.Get(a.GetNamespace()); ok {
		// we think the namespace was marked for deletion, but our current local cache says otherwise, we will force a live lookup.
		forceLiveLookup = exists && namespace.Status.Phase == v1.NamespaceActive
	}

	// refuse to operate on non-existent namespaces
	if !exists || forceLiveLookup {
		// as a last resort, make a call directly to storage
		namespace, err = l.client.CoreV1().Namespaces().Get(context.TODO(), a.GetNamespace(), metav1.GetOptions{})
		switch {
		case errors.IsNotFound(err):
			return err
		case err != nil:
			return errors.NewInternalError(err)
		}
		klog.V(4).Infof("found %s via storage lookup", a.GetNamespace())
	}

	// ensure that we're not trying to create objects in terminating namespaces
	if a.GetOperation() == admission.Create {
		if namespace.Status.Phase != v1.NamespaceTerminating {
			return nil
		}

		err := admission.NewForbidden(a, fmt.Errorf("unable to create new content in namespace %s because it is being terminated", a.GetNamespace()))
		if apierr, ok := err.(*errors.StatusError); ok {
			apierr.ErrStatus.Details.Causes = append(apierr.ErrStatus.Details.Causes, metav1.StatusCause{
				Type:    v1.NamespaceTerminatingCause,
				Message: fmt.Sprintf("namespace %s is being terminated", a.GetNamespace()),
				Field:   "metadata.namespace",
			})
		}
		return err
	}

	return nil
}

// NewLifecycle creates a new namespace Lifecycle admission control handler
func NewLifecycle(immortalNamespaces sets.String) (*Lifecycle, error) {
	return newLifecycleWithClock(immortalNamespaces, clock.RealClock{})
}

func newLifecycleWithClock(immortalNamespaces sets.String, clock utilcache.Clock) (*Lifecycle, error) {
	forceLiveLookupCache := utilcache.NewLRUExpireCacheWithClock(100, clock)
	return &Lifecycle{
		Handler:              admission.NewHandler(admission.Create, admission.Update, admission.Delete),
		immortalNamespaces:   immortalNamespaces,
		forceLiveLookupCache: forceLiveLookupCache,
	}, nil
}

// SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface.
func (l *Lifecycle) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
	namespaceInformer := f.Core().V1().Namespaces()
	l.namespaceLister = namespaceInformer.Lister()
	l.SetReadyFunc(namespaceInformer.Informer().HasSynced)
}

// SetExternalKubeClientSet implements the WantsExternalKubeClientSet interface.
func (l *Lifecycle) SetExternalKubeClientSet(client kubernetes.Interface) {
	l.client = client
}

// ValidateInitialization implements the InitializationValidator interface.
func (l *Lifecycle) ValidateInitialization() error {
	if l.namespaceLister == nil {
		return fmt.Errorf("missing namespaceLister")
	}
	if l.client == nil {
		return fmt.Errorf("missing client")
	}
	return nil
}

// accessReviewResources are resources which give a view into permissions in a namespace.  Users must be allowed to create these
// resources because returning "not found" errors allows someone to search for the "people I'm going to fire in 2017" namespace.
var accessReviewResources = map[schema.GroupResource]bool{
	{Group: "authorization.k8s.io", Resource: "localsubjectaccessreviews"}: true,
}

func isAccessReview(a admission.Attributes) bool {
	return accessReviewResources[a.GetResource().GroupResource()]
}
<commit_msg>UPSTREAM: <carry>: kube-apiserver: add our immortal namespaces directly to admission plugin<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package lifecycle

import (
	"context"
	"fmt"
	"io"
	"time"

	"k8s.io/klog/v2"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	utilcache "k8s.io/apimachinery/pkg/util/cache"
	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apiserver/pkg/admission"
	"k8s.io/apiserver/pkg/admission/initializer"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	corelisters "k8s.io/client-go/listers/core/v1"
)

const (
	// PluginName indicates the name of admission plug-in
	PluginName = "NamespaceLifecycle"
	// how long a namespace stays in the force live lookup cache before expiration.
	forceLiveLookupTTL = 30 * time.Second
	// how long to wait for a missing namespace before re-checking the cache (and then doing a live lookup)
	// this accomplishes two things:
	// 1. It allows a watch-fed cache time to observe a namespace creation event
	// 2. It allows time for a namespace creation to distribute to members of a storage cluster,
	//    so the live lookup has a better chance of succeeding even if it isn't performed against the leader.
	missingNamespaceWait = 50 * time.Millisecond
)

// Register registers a plugin
func Register(plugins *admission.Plugins) {
	plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
		return NewLifecycle(sets.NewString(metav1.NamespaceDefault, metav1.NamespaceSystem, metav1.NamespacePublic,
			// user specified configuration that cannot be rebuilt
			"openshift-config",
			// cluster generated configuration that cannot be rebuilt (etcd encryption keys)
			"openshift-config-managed",
			// the CVO which is the root we use to rebuild all the rest
			"openshift-cluster-version",
			// contains a namespaced list of all nodes in the cluster (yeah, weird.  they do it for multi-tenant management I think?)
			"openshift-machine-api",
		))
	})
}

// Lifecycle is an implementation of admission.Interface.
// It enforces life-cycle constraints around a Namespace depending on its Phase
type Lifecycle struct {
	*admission.Handler
	client             kubernetes.Interface
	immortalNamespaces sets.String
	namespaceLister    corelisters.NamespaceLister
	// forceLiveLookupCache holds a list of entries for namespaces that we have a strong reason to believe are stale in our local cache.
	// if a namespace is in this cache, then we will ignore our local state and always fetch latest from api server.
	forceLiveLookupCache *utilcache.LRUExpireCache
}

var _ = initializer.WantsExternalKubeInformerFactory(&Lifecycle{})
var _ = initializer.WantsExternalKubeClientSet(&Lifecycle{})

// Admit makes an admission decision based on the request attributes
func (l *Lifecycle) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
	// prevent deletion of immortal namespaces
	if a.GetOperation() == admission.Delete && a.GetKind().GroupKind() == v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() && l.immortalNamespaces.Has(a.GetName()) {
		return errors.NewForbidden(a.GetResource().GroupResource(), a.GetName(), fmt.Errorf("this namespace may not be deleted"))
	}

	// always allow non-namespaced resources
	if len(a.GetNamespace()) == 0 && a.GetKind().GroupKind() != v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() {
		return nil
	}

	if a.GetKind().GroupKind() == v1.SchemeGroupVersion.WithKind("Namespace").GroupKind() {
		// if a namespace is deleted, we want to prevent all further creates into it
		// while it is undergoing termination.  to reduce incidences where the cache
		// is slow to update, we add the namespace into a force live lookup list to ensure
		// we are not looking at stale state.
		if a.GetOperation() == admission.Delete {
			l.forceLiveLookupCache.Add(a.GetName(), true, forceLiveLookupTTL)
		}
		// allow all operations to namespaces
		return nil
	}

	// always allow deletion of other resources
	if a.GetOperation() == admission.Delete {
		return nil
	}

	// always allow access review checks.  Returning status about the namespace would be leaking information
	if isAccessReview(a) {
		return nil
	}

	// we need to wait for our caches to warm
	if !l.WaitForReady() {
		return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request"))
	}

	var (
		exists bool
		err    error
	)

	namespace, err := l.namespaceLister.Get(a.GetNamespace())
	if err != nil {
		if !errors.IsNotFound(err) {
			return errors.NewInternalError(err)
		}
	} else {
		exists = true
	}

	if !exists && a.GetOperation() == admission.Create {
		// give the cache time to observe the namespace before rejecting a create.
		// this helps when creating a namespace and immediately creating objects within it.
		time.Sleep(missingNamespaceWait)
		namespace, err = l.namespaceLister.Get(a.GetNamespace())
		switch {
		case errors.IsNotFound(err):
			// no-op
		case err != nil:
			return errors.NewInternalError(err)
		default:
			exists = true
		}
		if exists {
			klog.V(4).Infof("found %s in cache after waiting", a.GetNamespace())
		}
	}

	// forceLiveLookup if true will skip looking at local cache state and instead always make a live call to server.
	forceLiveLookup := false
	if _, ok := l.forceLiveLookupCache.Get(a.GetNamespace()); ok {
		// we think the namespace was marked for deletion, but our current local cache says otherwise, we will force a live lookup.
		forceLiveLookup = exists && namespace.Status.Phase == v1.NamespaceActive
	}

	// refuse to operate on non-existent namespaces
	if !exists || forceLiveLookup {
		// as a last resort, make a call directly to storage
		namespace, err = l.client.CoreV1().Namespaces().Get(context.TODO(), a.GetNamespace(), metav1.GetOptions{})
		switch {
		case errors.IsNotFound(err):
			return err
		case err != nil:
			return errors.NewInternalError(err)
		}
		klog.V(4).Infof("found %s via storage lookup", a.GetNamespace())
	}

	// ensure that we're not trying to create objects in terminating namespaces
	if a.GetOperation() == admission.Create {
		if namespace.Status.Phase != v1.NamespaceTerminating {
			return nil
		}

		err := admission.NewForbidden(a, fmt.Errorf("unable to create new content in namespace %s because it is being terminated", a.GetNamespace()))
		if apierr, ok := err.(*errors.StatusError); ok {
			apierr.ErrStatus.Details.Causes = append(apierr.ErrStatus.Details.Causes, metav1.StatusCause{
				Type:    v1.NamespaceTerminatingCause,
				Message: fmt.Sprintf("namespace %s is being terminated", a.GetNamespace()),
				Field:   "metadata.namespace",
			})
		}
		return err
	}

	return nil
}

// NewLifecycle creates a new namespace Lifecycle admission control handler
func NewLifecycle(immortalNamespaces sets.String) (*Lifecycle, error) {
	return newLifecycleWithClock(immortalNamespaces, clock.RealClock{})
}

func newLifecycleWithClock(immortalNamespaces sets.String, clock utilcache.Clock) (*Lifecycle, error) {
	forceLiveLookupCache := utilcache.NewLRUExpireCacheWithClock(100, clock)
	return &Lifecycle{
		Handler:              admission.NewHandler(admission.Create, admission.Update, admission.Delete),
		immortalNamespaces:   immortalNamespaces,
		forceLiveLookupCache: forceLiveLookupCache,
	}, nil
}

// SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface.
func (l *Lifecycle) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
	namespaceInformer := f.Core().V1().Namespaces()
	l.namespaceLister = namespaceInformer.Lister()
	l.SetReadyFunc(namespaceInformer.Informer().HasSynced)
}

// SetExternalKubeClientSet implements the WantsExternalKubeClientSet interface.
func (l *Lifecycle) SetExternalKubeClientSet(client kubernetes.Interface) {
	l.client = client
}

// ValidateInitialization implements the InitializationValidator interface.
func (l *Lifecycle) ValidateInitialization() error {
	if l.namespaceLister == nil {
		return fmt.Errorf("missing namespaceLister")
	}
	if l.client == nil {
		return fmt.Errorf("missing client")
	}
	return nil
}

// accessReviewResources are resources which give a view into permissions in a namespace.  Users must be allowed to create these
// resources because returning "not found" errors allows someone to search for the "people I'm going to fire in 2017" namespace.
var accessReviewResources = map[schema.GroupResource]bool{
	{Group: "authorization.k8s.io", Resource: "localsubjectaccessreviews"}: true,
}

func isAccessReview(a admission.Attributes) bool {
	return accessReviewResources[a.GetResource().GroupResource()]
}
<|endoftext|>