<commit_before>package api

import (
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"html/template"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/beevik/etree"
	"github.com/netlify/gotrue/conf"
	"github.com/netlify/gotrue/models"
	dsig "github.com/russellhaering/goxmldsig"
	uuid "github.com/satori/go.uuid"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

type ExternalSamlTestSuite struct {
	suite.Suite
	API        *API
	Config     *conf.Configuration
	instanceID uuid.UUID
}

func TestExternalSaml(t *testing.T) {
	api, config, instanceID, err := setupAPIForTestForInstance()
	require.NoError(t, err)

	ts := &ExternalSamlTestSuite{
		API:        api,
		Config:     config,
		instanceID: instanceID,
	}
	defer api.db.Close()

	suite.Run(t, ts)
}

func (ts *ExternalSamlTestSuite) SetupTest() {
	models.TruncateAll(ts.API.db)
}

func (ts *ExternalSamlTestSuite) docFromTemplate(path string, data interface{}) *etree.Document {
	doc := etree.NewDocument()

	templ, err := template.ParseFiles(path)
	ts.Require().NoError(err)
	read, write := io.Pipe()
	go func() {
		defer write.Close()
		err := templ.Execute(write, data)
		ts.Require().NoError(err)
	}()
	_, err = doc.ReadFrom(read)
	ts.Require().NoError(err)

	return doc
}

func (ts *ExternalSamlTestSuite) setupSamlExampleResponse(keyStore dsig.X509KeyStore) string {
	path := filepath.Join("testdata", "saml-response.xml")
	type ResponseParams struct {
		Now       string
		NotBefore string
		NotAfter  string
	}
	now := time.Now()
	doc := ts.docFromTemplate(path, ResponseParams{
		Now:       now.Format(time.RFC3339),
		NotBefore: now.Add(-5 * time.Minute).Format(time.RFC3339),
		NotAfter:  now.Add(5 * time.Minute).Format(time.RFC3339),
	})

	// sign
	resp := doc.SelectElement("Response")
	ctx := dsig.NewDefaultSigningContext(keyStore)
	sig, err := ctx.ConstructSignature(resp, true)
	ts.Require().NoError(err, "Response signature failed")
	respWithSig := resp.Copy()
	var children []etree.Token
	children = append(children, respWithSig.Child[0])     // issuer is always first
	children = append(children, sig)                      // next is the signature
	children = append(children, respWithSig.Child[1:]...) // then all other children
	respWithSig.Child = children
	doc.SetRoot(respWithSig)

	docRaw, err := doc.WriteToBytes()
	ts.Require().NoError(err)

	return base64.StdEncoding.EncodeToString(docRaw)
}

func (ts *ExternalSamlTestSuite) setupSamlExampleState() string {
	req := httptest.NewRequest(http.MethodGet, "http://localhost/authorize?provider=saml", nil)
	w := httptest.NewRecorder()
	ts.API.handler.ServeHTTP(w, req)
	ts.Require().Equal(http.StatusFound, w.Code)

	u, err := url.Parse(w.Header().Get("Location"))
	ts.Require().NoError(err, "redirect url parse failed")

	urlBase, _ := url.Parse(u.String())
	urlBase.RawQuery = ""
	ts.Equal(urlBase.String(), "https://idp/saml2test/redirect")

	q := u.Query()
	state := q.Get("RelayState")
	ts.Require().NotEmpty(state)
	return state
}

func (ts *ExternalSamlTestSuite) setupSamlMetadata() (*httptest.Server, dsig.X509KeyStore) {
	idpKeyStore := dsig.RandomKeyStoreForTest()
	_, idpCert, _ := idpKeyStore.GetKeyPair()

	path := filepath.Join("testdata", "saml-idp-metadata.xml")
	type MetadataParams struct {
		Cert string
	}
	doc := ts.docFromTemplate(path, MetadataParams{Cert: base64.StdEncoding.EncodeToString(idpCert)})
	metadata, err := doc.WriteToString()
	ts.Require().NoError(err)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/xml")
		w.WriteHeader(200)
		io.WriteString(w, metadata)
	}))
	return server, idpKeyStore
}

func (ts *ExternalSamlTestSuite) setupSamlSPCert() (string, string) {
	spKeyStore := dsig.RandomKeyStoreForTest()
	key, cert, _ := spKeyStore.GetKeyPair()
	keyBytes := pem.EncodeToMemory(&pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	})
	certBytes := pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE",
		Bytes: cert,
	})
	return string(keyBytes), string(certBytes)
}

func (ts *ExternalSamlTestSuite) TestSignupExternalSaml_Callback() {
	server, idpKeyStore := ts.setupSamlMetadata()
	defer server.Close()
	ts.Config.External.Saml.MetadataURL = server.URL

	key, cert := ts.setupSamlSPCert()
	ts.Config.External.Saml.SigningKey = key
	ts.Config.External.Saml.SigningCert = cert

	form := url.Values{}
	form.Add("RelayState", ts.setupSamlExampleState())
	form.Add("SAMLResponse", ts.setupSamlExampleResponse(idpKeyStore))
	req := httptest.NewRequest(http.MethodPost, "http://localhost/saml/acs", strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	w := httptest.NewRecorder()
	ts.API.handler.ServeHTTP(w, req)

	ts.Require().Equal(http.StatusFound, w.Code)

	u, err := url.Parse(w.Header().Get("Location"))
	ts.Require().NoError(err, "redirect url parse failed")

	v, err := url.ParseQuery(u.Fragment)
	ts.Require().NoError(err)
	ts.Empty(v.Get("error_description"))
	ts.Empty(v.Get("error"))

	ts.NotEmpty(v.Get("access_token"))
	ts.NotEmpty(v.Get("refresh_token"))
	ts.NotEmpty(v.Get("expires_in"))
	ts.Equal("bearer", v.Get("token_type"))

	// ensure user has been created
	_, err = models.FindUserByEmailAndAudience(ts.API.db, ts.instanceID, "saml@example.com", ts.Config.JWT.Aud)
	ts.Require().NoError(err)
}
<commit_msg>Add e2e test for saml metadata<commit_after>package api

import (
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"encoding/xml"
	"html/template"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/beevik/etree"
	"github.com/netlify/gotrue/conf"
	"github.com/netlify/gotrue/models"
	"github.com/russellhaering/gosaml2/types"
	dsig "github.com/russellhaering/goxmldsig"
	uuid "github.com/satori/go.uuid"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

type ExternalSamlTestSuite struct {
	suite.Suite
	API        *API
	Config     *conf.Configuration
	instanceID uuid.UUID
}

func TestExternalSaml(t *testing.T) {
	api, config, instanceID, err := setupAPIForTestForInstance()
	require.NoError(t, err)

	ts := &ExternalSamlTestSuite{
		API:        api,
		Config:     config,
		instanceID: instanceID,
	}
	defer api.db.Close()

	suite.Run(t, ts)
}

func (ts *ExternalSamlTestSuite) SetupTest() {
	models.TruncateAll(ts.API.db)
}

func (ts *ExternalSamlTestSuite) docFromTemplate(path string, data interface{}) *etree.Document {
	doc := etree.NewDocument()

	templ, err := template.ParseFiles(path)
	ts.Require().NoError(err)
	read, write := io.Pipe()
	go func() {
		defer write.Close()
		err := templ.Execute(write, data)
		ts.Require().NoError(err)
	}()
	_, err = doc.ReadFrom(read)
	ts.Require().NoError(err)

	return doc
}

func (ts *ExternalSamlTestSuite) setupSamlExampleResponse(keyStore dsig.X509KeyStore) string {
	path := filepath.Join("testdata", "saml-response.xml")
	type ResponseParams struct {
		Now       string
		NotBefore string
		NotAfter  string
	}
	now := time.Now()
	doc := ts.docFromTemplate(path, ResponseParams{
		Now:       now.Format(time.RFC3339),
		NotBefore: now.Add(-5 * time.Minute).Format(time.RFC3339),
		NotAfter:  now.Add(5 * time.Minute).Format(time.RFC3339),
	})

	// sign
	resp := doc.SelectElement("Response")
	ctx := dsig.NewDefaultSigningContext(keyStore)
	sig, err := ctx.ConstructSignature(resp, true)
	ts.Require().NoError(err, "Response signature failed")
	respWithSig := resp.Copy()
	var children []etree.Token
	children = append(children, respWithSig.Child[0])     // issuer is always first
	children = append(children, sig)                      // next is the signature
	children = append(children, respWithSig.Child[1:]...) // then all other children
	respWithSig.Child = children
	doc.SetRoot(respWithSig)

	docRaw, err := doc.WriteToBytes()
	ts.Require().NoError(err)

	return base64.StdEncoding.EncodeToString(docRaw)
}

func (ts *ExternalSamlTestSuite) setupSamlExampleState() string {
	req := httptest.NewRequest(http.MethodGet, "http://localhost/authorize?provider=saml", nil)
	w := httptest.NewRecorder()
	ts.API.handler.ServeHTTP(w, req)
	ts.Require().Equal(http.StatusFound, w.Code)

	u, err := url.Parse(w.Header().Get("Location"))
	ts.Require().NoError(err, "redirect url parse failed")

	urlBase, _ := url.Parse(u.String())
	urlBase.RawQuery = ""
	ts.Equal(urlBase.String(), "https://idp/saml2test/redirect")

	q := u.Query()
	state := q.Get("RelayState")
	ts.Require().NotEmpty(state)
	return state
}

func (ts *ExternalSamlTestSuite) setupSamlMetadata() (*httptest.Server, dsig.X509KeyStore) {
	idpKeyStore := dsig.RandomKeyStoreForTest()
	_, idpCert, _ := idpKeyStore.GetKeyPair()

	path := filepath.Join("testdata", "saml-idp-metadata.xml")
	type MetadataParams struct {
		Cert string
	}
	doc := ts.docFromTemplate(path, MetadataParams{Cert: base64.StdEncoding.EncodeToString(idpCert)})
	metadata, err := doc.WriteToString()
	ts.Require().NoError(err)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/xml")
		w.WriteHeader(200)
		io.WriteString(w, metadata)
	}))
	return server, idpKeyStore
}

func (ts *ExternalSamlTestSuite) setupSamlSPCert() (string, string) {
	spKeyStore := dsig.RandomKeyStoreForTest()
	key, cert, _ := spKeyStore.GetKeyPair()
	keyBytes := pem.EncodeToMemory(&pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	})
	certBytes := pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE",
		Bytes: cert,
	})
	return string(keyBytes), string(certBytes)
}

func (ts *ExternalSamlTestSuite) TestSignupExternalSaml_Callback() {
	server, idpKeyStore := ts.setupSamlMetadata()
	defer server.Close()
	ts.Config.External.Saml.MetadataURL = server.URL

	key, cert := ts.setupSamlSPCert()
	ts.Config.External.Saml.SigningKey = key
	ts.Config.External.Saml.SigningCert = cert

	form := url.Values{}
	form.Add("RelayState", ts.setupSamlExampleState())
	form.Add("SAMLResponse", ts.setupSamlExampleResponse(idpKeyStore))
	req := httptest.NewRequest(http.MethodPost, "http://localhost/saml/acs", strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	w := httptest.NewRecorder()
	ts.API.handler.ServeHTTP(w, req)

	ts.Require().Equal(http.StatusFound, w.Code)

	u, err := url.Parse(w.Header().Get("Location"))
	ts.Require().NoError(err, "redirect url parse failed")

	v, err := url.ParseQuery(u.Fragment)
	ts.Require().NoError(err)
	ts.Empty(v.Get("error_description"))
	ts.Empty(v.Get("error"))

	ts.NotEmpty(v.Get("access_token"))
	ts.NotEmpty(v.Get("refresh_token"))
	ts.NotEmpty(v.Get("expires_in"))
	ts.Equal("bearer", v.Get("token_type"))

	// ensure user has been created
	_, err = models.FindUserByEmailAndAudience(ts.API.db, ts.instanceID, "saml@example.com", ts.Config.JWT.Aud)
	ts.Require().NoError(err)
}

func (ts *ExternalSamlTestSuite) TestMetadata() {
	server, _ := ts.setupSamlMetadata()
	defer server.Close()
	ts.Config.External.Saml.MetadataURL = server.URL

	key, cert := ts.setupSamlSPCert()
	ts.Config.External.Saml.SigningKey = key
	ts.Config.External.Saml.SigningCert = cert

	req := httptest.NewRequest(http.MethodGet, "http://localhost/saml/metadata", nil)
	w := httptest.NewRecorder()
	ts.API.handler.ServeHTTP(w, req)

	ts.Require().Equal(http.StatusOK, w.Code)

	md := &types.EntityDescriptor{}
	err := xml.NewDecoder(w.Body).Decode(md)
	ts.Require().NoError(err)

	ts.Equal("http://localhost/saml", md.EntityID)
	for _, acs := range md.SPSSODescriptor.AssertionConsumerServices {
		if acs.Binding == "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" {
			ts.Equal("http://localhost/saml/acs", acs.Location)
			break
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"go/parser"
	"go/printer"
	"go/ast"
	"go/token"
	"log"
	"os"
)

var (
	srcFn   = flag.String("src", "", "source filename")
	getName = flag.String("name", "", "func/type name to output")
	html    = flag.Bool("html", true, "output HTML")
	showPkg = flag.Bool("pkg", false, "show package in output")
)

func main() {
	// handle input
	flag.Parse()
	if *srcFn == "" || *getName == "" {
		flag.Usage()
		os.Exit(2)
	}
	// load file
	fs := token.NewFileSet()
	file, err := parser.ParseFile(fs, *srcFn, nil, 0)
	if err != nil {
		log.Fatal(err)
	}
	// create printer
	p := &printer.Config{
		Mode:     0,
		Tabwidth: 8,
	}
	// create filter
	filter := func(name string) bool {
		return name == *getName
	}
	// filter
	if !ast.FilterFile(file, filter) {
		os.Exit(1)
	}
	b := new(bytes.Buffer)
	p.Fprint(b, fs, file)
	// drop package declaration
	if !*showPkg {
		for {
			c, err := b.ReadByte()
			if c == '\n' || err != nil {
				break
			}
		}
	}
	// drop leading newlines
	for {
		b, err := b.ReadByte()
		if err != nil {
			break
		}
		if b != '\n' {
			os.Stdout.Write([]byte{b})
			break
		}
	}
	// output
	b.WriteTo(os.Stdout)
}
<commit_msg>srcextract: HTML-escape output if so desired<commit_after>package main

import (
	"bytes"
	"flag"
	"go/parser"
	"go/printer"
	"go/ast"
	"go/token"
	"log"
	"os"
	"template"
)

var (
	srcFn   = flag.String("src", "", "source filename")
	getName = flag.String("name", "", "func/type name to output")
	html    = flag.Bool("html", true, "output HTML")
	showPkg = flag.Bool("pkg", false, "show package in output")
)

func main() {
	// handle input
	flag.Parse()
	if *srcFn == "" || *getName == "" {
		flag.Usage()
		os.Exit(2)
	}
	// load file
	fs := token.NewFileSet()
	file, err := parser.ParseFile(fs, *srcFn, nil, 0)
	if err != nil {
		log.Fatal(err)
	}
	// create filter
	filter := func(name string) bool {
		return name == *getName
	}
	// filter
	if !ast.FilterFile(file, filter) {
		os.Exit(1)
	}
	// print the AST
	var b bytes.Buffer
	printer.Fprint(&b, fs, file)
	// drop package declaration
	if !*showPkg {
		for {
			c, err := b.ReadByte()
			if c == '\n' || err != nil {
				break
			}
		}
	}
	// drop leading newlines
	for {
		b, err := b.ReadByte()
		if err != nil {
			break
		}
		if b != '\n' {
			os.Stdout.Write([]byte{b})
			break
		}
	}
	// output
	if *html {
		template.HTMLEscape(os.Stdout, b.Bytes())
	} else {
		b.WriteTo(os.Stdout)
	}
}
<|endoftext|>


<commit_before>package portutil

import (
	"net"
	"testing"
)

func TestVerify(t *testing.T) {
	port, err := Verify(8080)
	if err != nil {
		t.Fatalf("Couldn't Verify port: %s. Error: %s", port, err)
	}
}

func TestVerifyHostPort(t *testing.T) {
	address, err := VerifyHostPort("127.0.0.1:8080")
	if err != nil {
		t.Fatalf("Couldn't VerifyHostPort: %s. Error: %s", address, err)
	}
}

func TestPortTaken(t *testing.T) {
	address := "127.0.0.1:8090"
	ln, err := net.Listen("tcp", address)
	if err != nil {
		t.Fatalf("Err creating listener", err)
	}
	defer ln.Close()

	address, err = VerifyHostPort(address)
	if err == nil {
		t.Fatalf("Failed to detect port was taken. Error: %s", err)
	}
}
<commit_msg>adding test for GetUnique<commit_after>package portutil

import (
	"net"
	"testing"
)

func TestVerify(t *testing.T) {
	port, err := Verify(8080)
	if err != nil {
		t.Errorf("Couldn't Verify port: %s. Error: %s", port, err)
	}
}

func TestVerifyHostPort(t *testing.T) {
	address, err := VerifyHostPort("127.0.0.1:8080")
	if err != nil {
		t.Errorf("Couldn't VerifyHostPort: %s. Error: %s", address, err)
	}
}

func TestGetUnique(t *testing.T) {
	_, err := GetUnique()
	if err != nil {
		t.Errorf("Err getting unique port", err)
	}
}

func TestPortTaken(t *testing.T) {
	uniqPort, _ := GetUnique()

	tests := []struct {
		port int
	}{
		{8090},
		{uniqPort},
	}

	for _, test := range tests {
		address := JoinHostPort("127.0.0.1", test.port)
		ln, err := net.Listen("tcp", address)
		if err != nil {
			t.Fatalf("Err creating listener", err)
		}
		defer ln.Close()

		address, err = VerifyHostPort(address)
		if err == nil {
			t.Errorf("Failed to detect port was taken. Error: %s", err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Christian Funkhouser <christian.funkhouser@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package preppi

import "fmt"

var (
	// Version of PrepPi
	Version = "0.1.0"

	// BuildID of PrepPi. Usually overridden at build time.
	BuildID = "dev"
)

// VersionString returnds a human-readable PrepPi version ID.
func VersionString() string {
	return fmt.Sprintf("PrepPi v%v (%v)", Version, BuildID)
}
<commit_msg>Make the version hardcoded in preppi.go a little less specific.<commit_after>// Copyright (c) 2017 Christian Funkhouser <christian.funkhouser@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package preppi

import "fmt"

var (
	// Version of PrepPi. Usually overridden at build time.
	Version = "0.1.dev"

	// BuildID of PrepPi. Usually overridden at build time.
	BuildID = "dev"
)

// VersionString returnds a human-readable PrepPi version ID.
func VersionString() string {
	return fmt.Sprintf("PrepPi v%v (%v)", Version, BuildID)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path"
	"syscall"

	"github.com/jweslley/procker"
)

const defaultEnvfile = ".env"

var (
	cmdStart = &command{
		desc: "Start application's processes",
		help: `Usage: procker start [options]

Start the processes specified by a Procfile

Available options:`,
		exec: start,
		flag: startFlags}

	// flags
	startFlags    = flag.NewFlagSet("start", flag.ExitOnError)
	startProcfile = startFlags.String("f", "Procfile",
		"Procfile declaring commands to run")
	startEnvfile = startFlags.String("e", defaultEnvfile,
		"File containing environment variables to be used")
	startBasePort = startFlags.Int("p", 5000,
		"Base port to be used by processes. Should be a multiple of 1000")
)

func start(args []string) {
	procSpecs := parseProfile(*startProcfile)
	env := parseEnv(*startEnvfile)
	dir := path.Dir(*startProcfile)
	padding := longestName(procSpecs)
	log.SetOutput(procker.NewPrefixedWriter(os.Stdout, prefix(programName, padding)))
	process := buildProcess(procSpecs, dir, env, *startBasePort, padding)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		for sig := range c {
			log.Printf("%v received, stopping processes and exiting.", sig)
			process.Kill()
			os.Exit(1)
		}
	}()

	err := process.Start()
	failIf(err)

	err = process.Wait()
	failIf(err)
}

func buildProcess(
	specs map[string]string,
	dir string,
	env []string,
	basePort int,
	padding int) procker.Process {

	i := 0
	p := []procker.Process{}
	for name, command := range specs {
		port := basePort + (i * 100)
		process := procker.NewProcess(name,
			command,
			dir,
			append(env, fmt.Sprintf("PORT=%d", port)),
			procker.NewPrefixedWriter(os.Stdout, prefix(name, padding)),
			procker.NewPrefixedWriter(os.Stderr, prefix(name, padding)))
		p = append(p, process)
		i++

		log.Printf("starting %s on port %d", name, port)
	}
	return procker.NewProcessSet(p...)
}

func parseProfile(filepath string) map[string]string {
	file, err := os.Open(filepath)
	defer file.Close()
	failIf(err)

	processes, err := procker.ParseProcfile(file)
	failIf(err)
	return processes
}

func parseEnv(filepath string) []string {
	file, err := os.Open(filepath)
	defer file.Close()
	if err != nil {
		if filepath == defaultEnvfile {
			return []string{}
		} else {
			failIf(err)
		}
	}

	env, err := procker.ParseEnv(file)
	failIf(err)
	return env
}

func longestName(processes map[string]string) int {
	max := len(programName)
	for name := range processes {
		if len(name) > max {
			max = len(name)
		}
	}
	return max
}

func prefix(prefix string, padding int) string {
	return fmt.Sprintf(fmt.Sprintf("%%%ds | ", -padding), prefix)
}
<commit_msg>assign sequential port numbers<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path"
	"syscall"

	"github.com/jweslley/procker"
)

const defaultEnvfile = ".env"

var (
	cmdStart = &command{
		desc: "Start application's processes",
		help: `Usage: procker start [options]

Start the processes specified by a Procfile

Available options:`,
		exec: start,
		flag: startFlags}

	// flags
	startFlags    = flag.NewFlagSet("start", flag.ExitOnError)
	startProcfile = startFlags.String("f", "Procfile",
		"Procfile declaring commands to run")
	startEnvfile = startFlags.String("e", defaultEnvfile,
		"File containing environment variables to be used")
	startBasePort = startFlags.Int("p", 5000,
		"Base port to be used by processes. Should be a multiple of 1000")
)

func start(args []string) {
	procSpecs := parseProfile(*startProcfile)
	env := parseEnv(*startEnvfile)
	dir := path.Dir(*startProcfile)
	padding := longestName(procSpecs)
	log.SetOutput(procker.NewPrefixedWriter(os.Stdout, prefix(programName, padding)))
	process := buildProcess(procSpecs, dir, env, *startBasePort, padding)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		for sig := range c {
			log.Printf("%v received, stopping processes and exiting.", sig)
			process.Kill()
			os.Exit(1)
		}
	}()

	err := process.Start()
	failIf(err)

	err = process.Wait()
	failIf(err)
}

func buildProcess(
	specs map[string]string,
	dir string,
	env []string,
	port int,
	padding int) procker.Process {

	p := []procker.Process{}
	for name, command := range specs {
		process := procker.NewProcess(name,
			command,
			dir,
			append(env, fmt.Sprintf("PORT=%d", port)),
			procker.NewPrefixedWriter(os.Stdout, prefix(name, padding)),
			procker.NewPrefixedWriter(os.Stderr, prefix(name, padding)))

		log.Printf("starting %s on port %d", name, port)
		p = append(p, process)
		port++
	}
	return procker.NewProcessSet(p...)
}

func parseProfile(filepath string) map[string]string {
	file, err := os.Open(filepath)
	defer file.Close()
	failIf(err)

	processes, err := procker.ParseProcfile(file)
	failIf(err)
	return processes
}

func parseEnv(filepath string) []string {
	file, err := os.Open(filepath)
	defer file.Close()
	if err != nil {
		if filepath == defaultEnvfile {
			return []string{}
		} else {
			failIf(err)
		}
	}

	env, err := procker.ParseEnv(file)
	failIf(err)
	return env
}

func longestName(processes map[string]string) int {
	max := len(programName)
	for name := range processes {
		if len(name) > max {
			max = len(name)
		}
	}
	return max
}

func prefix(prefix string, padding int) string {
	return fmt.Sprintf(fmt.Sprintf("%%%ds | ", -padding), prefix)
}
<|endoftext|>


<commit_before>package persistence

import (
	"log"

	"github.com/AsynkronIT/gam/actor"
	proto "github.com/golang/protobuf/proto"
)

type persistent interface {
	init(provider Provider, context actor.Context)
	PersistReceive(message proto.Message)
	PersistSnapshot(snapshot proto.Message)
	Recovering() bool
	Name() string
}

type Mixin struct {
	eventIndex int
	provider   Provider
	context    actor.Context
	recovering bool
}

func (mixin *Mixin) Recovering() bool {
	return mixin.recovering
}

func (mixin *Mixin) Name() string {
	name := mixin.context.Self().Id
	return name
}
func (mixin *Mixin) PersistReceive(message proto.Message) {

	mixin.provider.PersistEvent(mixin.Name(), mixin.eventIndex, message)
	mixin.eventIndex++
	mixin.context.Receive(message)
	if mixin.eventIndex%mixin.provider.GetSnapshotInterval() == 0 {
		log.Println("Requesting snapshot")
		mixin.context.Receive(&RequestSnapshot{})
	}
}

func (mixin *Mixin) PersistSnapshot(snapshot proto.Message) {
	log.Println("Persisting snapshot")
	mixin.provider.PersistSnapshot(mixin.Name(), mixin.eventIndex, snapshot)
}

func (mixin *Mixin) init(provider Provider, context actor.Context) {
	mixin.eventIndex = 0
	mixin.context = context
	mixin.provider = provider
	mixin.recovering = true

	if snapshot, eventIndex, ok := provider.GetSnapshot(mixin.Name()); ok {
		mixin.eventIndex = eventIndex
		context.Receive(snapshot)
	}
	mixin.provider.GetEvents(mixin.Name(), mixin.eventIndex, func(e interface{}) {
		context.Receive(e)
		mixin.eventIndex++
	})
	mixin.recovering = false
	context.Receive(&ReplayComplete{})
}
<commit_msg>remove logs<commit_after>package persistence

import (
	"github.com/AsynkronIT/gam/actor"
	proto "github.com/golang/protobuf/proto"
)

type persistent interface {
	init(provider Provider, context actor.Context)
	PersistReceive(message proto.Message)
	PersistSnapshot(snapshot proto.Message)
	Recovering() bool
	Name() string
}

type Mixin struct {
	eventIndex int
	provider   Provider
	context    actor.Context
	recovering bool
}

func (mixin *Mixin) Recovering() bool {
	return mixin.recovering
}

func (mixin *Mixin) Name() string {
	name := mixin.context.Self().Id
	return name
}
func (mixin *Mixin) PersistReceive(message proto.Message) {

	mixin.provider.PersistEvent(mixin.Name(), mixin.eventIndex, message)
	mixin.eventIndex++
	mixin.context.Receive(message)
	if mixin.eventIndex%mixin.provider.GetSnapshotInterval() == 0 {
		mixin.context.Receive(&RequestSnapshot{})
	}
}

func (mixin *Mixin) PersistSnapshot(snapshot proto.Message) {
	mixin.provider.PersistSnapshot(mixin.Name(), mixin.eventIndex, snapshot)
}

func (mixin *Mixin) init(provider Provider, context actor.Context) {
	mixin.eventIndex = 0
	mixin.context = context
	mixin.provider = provider
	mixin.recovering = true

	if snapshot, eventIndex, ok := provider.GetSnapshot(mixin.Name()); ok {
		mixin.eventIndex = eventIndex
		context.Receive(snapshot)
	}
	mixin.provider.GetEvents(mixin.Name(), mixin.eventIndex, func(e interface{}) {
		context.Receive(e)
		mixin.eventIndex++
	})
	mixin.recovering = false
	context.Receive(&ReplayComplete{})
}
<|endoftext|>


<commit_before>// Package kodingcontext provides manages koding specific operations on top of
// terraform
package kodingcontext

import (
	"bytes"
	"errors"

	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/cli"
)

func newKodingContext(sc <-chan struct{}) *KodingContext {
	b := new(bytes.Buffer)

	return &KodingContext{
		Buffer:       b,
		ui:           NewUI(b),
		ShutdownChan: sc,
	}
}

// Context holds the required operational parameters for any kind of terraform
// call
type KodingContext struct {
	context

	Buffer       *bytes.Buffer
	ui           *cli.PrefixedUi
	Variables    map[string]string
	ShutdownChan <-chan struct{}
	ContentID    string
}

// TerraformContextOpts creates a basic context options for terraform itself
func (c *KodingContext) TerraformContextOpts() *terraform.ContextOpts {
	return c.TerraformContextOptsWithPlan(nil)
}

// TerraformContextOptsWithPlan creates a new context out of a given plan
func (c *KodingContext) TerraformContextOptsWithPlan(p *terraform.Plan) *terraform.ContextOpts {
	if p == nil {
		p = &terraform.Plan{}
	}

	return &terraform.ContextOpts{
		Destroy:     false,
		Parallelism: 0,

		Hooks: nil,

		Module: p.Module,
		State:  p.State,
		Diff:   p.Diff,

		Providers:    c.Providers,
		Provisioners: c.Provisioners,
		Variables:    c.Variables,
	}
}

// Close terminates the existing context
func (c *KodingContext) Close() error {
	if c.ContentID != "" {
		return errors.New("contentID is nil")
	}

	shutdownChansMu.Lock()
	delete(shutdownChans, c.ContentID)
	shutdownChansWG.Done()
	shutdownChansMu.Unlock()

	return c.LocalStorage.Remove(c.ContentID)
}
<commit_msg>kites/terraformer: fix a nasty bug<commit_after>// Package kodingcontext provides manages koding specific operations on top of
// terraform
package kodingcontext

import (
	"bytes"
	"errors"

	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/cli"
)

func newKodingContext(sc <-chan struct{}) *KodingContext {
	b := new(bytes.Buffer)

	return &KodingContext{
		Buffer:       b,
		ui:           NewUI(b),
		ShutdownChan: sc,
	}
}

// Context holds the required operational parameters for any kind of terraform
// call
type KodingContext struct {
	context

	Buffer       *bytes.Buffer
	ui           *cli.PrefixedUi
	Variables    map[string]string
	ShutdownChan <-chan struct{}
	ContentID    string
}

// TerraformContextOpts creates a basic context options for terraform itself
func (c *KodingContext) TerraformContextOpts() *terraform.ContextOpts {
	return c.TerraformContextOptsWithPlan(nil)
}

// TerraformContextOptsWithPlan creates a new context out of a given plan
func (c *KodingContext) TerraformContextOptsWithPlan(p *terraform.Plan) *terraform.ContextOpts {
	if p == nil {
		p = &terraform.Plan{}
	}

	return &terraform.ContextOpts{
		Destroy:     false,
		Parallelism: 0,

		Hooks: nil,

		Module: p.Module,
		State:  p.State,
		Diff:   p.Diff,

		Providers:    c.Providers,
		Provisioners: c.Provisioners,
		Variables:    c.Variables,
	}
}

// Close terminates the existing context
func (c *KodingContext) Close() error {
	if c.ContentID == "" {
		return errors.New("contentID is nil")
	}

	shutdownChansMu.Lock()
	delete(shutdownChans, c.ContentID)
	shutdownChansWG.Done()
	shutdownChansMu.Unlock()

	return c.LocalStorage.Remove(c.ContentID)
}
<|endoftext|>


<commit_before>package gson

import "bytes"
import "sort"
import "fmt"

// Value abstractions for golang-native value.
type Value struct {
	config *Config
	data   interface{}
}

// Data returns the golang data.
func (val *Value) Data() interface{} {
	return val.data
}

// Tojson encode golang native value to json text.
func (val *Value) Tojson(jsn *Json) *Json {
	out := jsn.data[jsn.n:cap(jsn.data)]
	jsn.n += value2json(val.data, out, val.config)
	return jsn
}

// Tocbor encode golang native into cbor binary.
func (val *Value) Tocbor(cbr *Cbor) *Cbor {
	out := cbr.data[cbr.n:cap(cbr.data)]
	cbr.n += value2cbor(val.data, out, val.config)
	return cbr
}

// Tocollate encode golang native into binary-collation.
func (val *Value) Tocollate(clt *Collate) *Collate {
	out := clt.data[clt.n:cap(clt.data)]
	clt.n += gson2collate(val.data, out, val.config)
	return clt
}

// ListPointers all possible pointers in value.
func (val *Value) ListPointers(ptrs []string) []string {
	prefix := val.config.pools.prefixPool.Get().([]byte)
	defer val.config.pools.prefixPool.Put(prefix[:0])
	ptrs = allpaths(val.data, ptrs, prefix)
	ptrs = append(ptrs, "")
	return ptrs
}

// Get field or nested field specified by json pointer.
func (val *Value) Get(jptr *Jsonpointer) (item interface{}) {
	return valGet(jptr.Segments(), val.data)
}

// Set field or nested field specified by json pointer. While
// `newval` is gauranteed to contain the `item`, `val` _may_ not be.
// Suggested usage,
//      val := config.NewValue([]interface{}{"hello"})
//      newval, _ = val.Set("/-", "world")
func (val *Value) Set(jptr *Jsonpointer, item interface{}) (newval, oldval interface{}) {
	return valSet(jptr.Segments(), val.data, item)
}

// Delete field or nested field specified by json pointer. While
// `newval` is gauranteed to be updated, `val` _may_ not be.
// Suggested usage,
//      val := NewValue([]interface{}{"hello", "world"})
//      newval, _ = val.Delete("/1")
func (val *Value) Delete(jptr *Jsonpointer) (newval, deleted interface{}) {
	return valDel(jptr.Segments(), val.data)
}

// Append item to end of an array pointed by json-pointer.
// returns `newval`, is gauranteed to be updated,
//      val := NewValue([]interface{}{"hello", "world"})
//      newval, _ = val.Append("", "welcome")
func (val *Value) Append(jptr *Jsonpointer, item interface{}) interface{} {
	return valAppend(jptr.Segments(), val.data, item)
}

// Prepend an item to the beginning of an array.
// returns `newval`, is gauranteed to be updated,
//      val := NewValue([]interface{}{"hello", "world"})
//      newval, _ = val.Append("", "welcome")
func (val *Value) Prepend(jptr *Jsonpointer, item interface{}) interface{} {
	return valPrepend(jptr.Segments(), val.data, item)
}

func (val *Value) Compare(other *Value) int {
	return valuecompare(val.data, other.data)
}

func valuecompare(v1, v2 interface{}) int {
	t1, t2 := valueType(v1), valueType(v2)
	if t1 < t2 {
		return -1

	} else if t1 > t2 {
		return 1

	} else {
		switch t1 {
		case TypeNull:
			return 0

		case TypeFalse:
			if t2 == TypeTrue {
				return -1
			}
			return 0

		case TypeTrue:
			if t2 == TypeFalse {
				return 1
			}
			return 0

		case TypeNumber:
			cmp := cmpnumber(v1, v2)
			return cmp

		case TypeString:
			b1 := str2bytes(v1.(string))
			b2 := str2bytes(v2.(string))
			return bytes.Compare(b1, b2)

		case TypeArray:
			a1, a2 := v1.([]interface{}), v2.([]interface{})
			for i, item := range a1 {
				if i > len(a2)-1 {
					break
				} else if cmp := valuecompare(item, a2[i]); cmp != 0 {
					return cmp
				}
			}
			if len(a1) < len(a2) {
				return -1
			} else if len(a1) > len(a2) {
				return 1
			}
			return 0

		case TypeObj:
			o1 := v1.(map[string]interface{})
			o2 := v2.(map[string]interface{})
			switch {
			case len(o1) < len(o2):
				return -1
			case len(o1) > len(o2):
				return 1
			}

			keys1 := make([]string, 0)
			for key := range o1 {
				keys1 = append(keys1, key)
			}
			sort.Strings(keys1)
			keys2 := make([]string, 0)
			for key := range o2 {
				keys2 = append(keys2, key)
			}
			sort.Strings(keys2)

			for i, key1 := range keys1 {
				key2 := keys2[i]
				val1, val2 := o1[key1], o2[key2]
				switch {
				case key1 < key2:
					return -1
				case key1 > key2:
					return 1
				case key1 == key2:
					cmp := valuecompare(val1, val2)
					if cmp != 0 {
						return cmp
					}
				}
			}
			return 0
		}
	}
	panic(fmt.Errorf("unknown value type: %T", v1))
}

func valueType(data interface{}) byte {
	if data == nil {
		return TypeNull
	}
	switch v := data.(type) {
	case bool:
		if v == false {
			return TypeFalse
		}
		return TypeTrue
	case float64:
		return TypeNumber
	case int64, uint64, int, uint:
		return TypeNumber
	case string:
		return TypeString
	case []interface{}:
		return TypeArray
	case map[string]interface{}:
		return TypeObj
	}
	panic(fmt.Errorf("unknown value type: %T", data))
}

func cmpnumber(n1, n2 interface{}) int {
	switch v1 := n1.(type) {
	case float64:
		return float64t(v1).cmp(n2)
	case int:
		return int64t(v1).cmp(n2)
	case int64:
		return int64t(v1).cmp(n2)
	case uint:
		return uint64t(v1).cmp(n2)
	case uint64:
		return uint64t(v1).cmp(n2)
	}
	panic(fmt.Errorf("unknown value type: %T", n1))
}

type float64t float64

func (this float64t) cmp(other interface{}) int {
	var rc int
	switch {
	case float64(this) < float64(int64(this)):
		rc = -1
	case float64(this) > float64(int64(this)):
		rc = 1
	}

	switch val := other.(type) {
	case float64:
		if float64(this) < val {
			return -1
		} else if float64(this) == val {
			return 0
		}
		return 1

	case int64:
		if v := int64(this); v < val {
			return -1
		} else if v == val {
			return rc
		}
		return 1

	case uint64:
		if v := uint64(this); v < val {
			return -1
		} else if v == val {
			return rc
		}
		return 1
	}
	panic(fmt.Errorf("cannot handle type %T", other))
}

type int64t int64

func (this int64t) cmp(other interface{}) int {
	switch val := other.(type) {
	case float64:
		return -float64t(val).cmp(int64(this))
	case int:
		if int64(this) < int64(val) {
			return -1
		} else if int64(this) == int64(val) {
			return 0
		}
		return 1

	case int64:
		if int64(this) < val {
			return -1
		} else if int64(this) == val {
			return 0
		}
		return 1

	case uint64:
		if val >= 9223372036854775808 {
			return -1
		} else if v := int64(val); this < int64t(v) {
			return -1
		} else if this == int64t(v) {
			return 0
		}
		return 1
	}
	panic(fmt.Errorf("cannot handle type %T", other))
}

type uint64t int64

func (this uint64t) cmp(other interface{}) int {
	switch val := other.(type) {
	case float64:
		return -float64t(val).cmp(uint64(this))
	case int64:
		if val < 0 {
			return 1
		} else if v := int64(val); int64(this) < v {
			return -1
		} else if int64(this) == v {
			return 0
		}
		return 1
	case uint64:
		if uint64(this) < val {
			return -1
		} else if uint64(this) == val {
			return 0
		}
		return 1
	}
	panic(fmt.Errorf("cannot handle type %T", other))
}
<commit_msg>cosmetics.<commit_after>package gson

import "bytes"
import "sort"
import "fmt"

// Value abstractions for golang-native value.
type Value struct {
	config *Config
	data   interface{}
}

// Data returns the golang data.
func (val *Value) Data() interface{} {
	return val.data
}

// Tojson encode golang native value to json text.
func (val *Value) Tojson(jsn *Json) *Json {
	out := jsn.data[jsn.n:cap(jsn.data)]
	jsn.n += value2json(val.data, out, val.config)
	return jsn
}

// Tocbor encode golang native into cbor binary.
func (val *Value) Tocbor(cbr *Cbor) *Cbor {
	out := cbr.data[cbr.n:cap(cbr.data)]
	cbr.n += value2cbor(val.data, out, val.config)
	return cbr
}

// Tocollate encode golang native into binary-collation.
func (val *Value) Tocollate(clt *Collate) *Collate {
	out := clt.data[clt.n:cap(clt.data)]
	clt.n += gson2collate(val.data, out, val.config)
	return clt
}

// ListPointers all possible pointers in value.
func (val *Value) ListPointers(ptrs []string) []string {
	prefix := val.config.pools.prefixPool.Get().([]byte)
	defer val.config.pools.prefixPool.Put(prefix[:0])
	ptrs = allpaths(val.data, ptrs, prefix)
	ptrs = append(ptrs, "")
	return ptrs
}

// Get field or nested field specified by json pointer.
func (val *Value) Get(jptr *Jsonpointer) (item interface{}) {
	return valGet(jptr.Segments(), val.data)
}

// Set field or nested field specified by json pointer. While
// `newval` is gauranteed to contain the `item`, `val` _may_ not be.
// Suggested usage,
//      val := config.NewValue([]interface{}{"hello"})
//      newval, _ = val.Set("/-", "world")
func (val *Value) Set(jptr *Jsonpointer, item interface{}) (newval, oldval interface{}) {
	return valSet(jptr.Segments(), val.data, item)
}

// Delete field or nested field specified by json pointer. While
// `newval` is gauranteed to be updated, `val` _may_ not be.
// Suggested usage,
//      val := NewValue([]interface{}{"hello", "world"})
//      newval, _ = val.Delete("/1")
func (val *Value) Delete(jptr *Jsonpointer) (newval, deleted interface{}) {
	return valDel(jptr.Segments(), val.data)
}

// Append item to end of an array pointed by json-pointer.
// returns `newval`, is gauranteed to be updated,
//      val := NewValue([]interface{}{"hello", "world"})
//      newval, _ = val.Append("", "welcome")
func (val *Value) Append(jptr *Jsonpointer, item interface{}) interface{} {
	return valAppend(jptr.Segments(), val.data, item)
}

// Prepend an item to the beginning of an array.
// returns `newval`, is gauranteed to be updated,
//      val := NewValue([]interface{}{"hello", "world"})
//      newval, _ = val.Append("", "welcome")
func (val *Value) Prepend(jptr *Jsonpointer, item interface{}) interface{} {
	return valPrepend(jptr.Segments(), val.data, item)
}

func (val *Value) Compare(other *Value) int {
	return valuecompare(val.data, other.data)
}

func valuecompare(v1, v2 interface{}) int {
	t1, t2 := valueType(v1), valueType(v2)
	if t1 < t2 {
		return -1

	} else if t1 > t2 {
		return 1

	} else {
		switch t1 {
		case TypeNull:
			return 0

		case TypeFalse:
			if t2 == TypeTrue {
				return -1
			}
			return 0

		case TypeTrue:
			if t2 == TypeFalse {
				return 1
			}
			return 0

		case TypeNumber:
			cmp := cmpnumber(v1, v2)
			return cmp

		case TypeString:
			b1 := str2bytes(v1.(string))
			b2 := str2bytes(v2.(string))
			return bytes.Compare(b1, b2)

		case TypeArray:
			a1, a2 := v1.([]interface{}), v2.([]interface{})
			for i, item := range a1 {
				if i > len(a2)-1 {
					break
				} else if cmp := valuecompare(item, a2[i]); cmp != 0 {
					return cmp
				}
			}
			if len(a1) < len(a2) {
				return -1
			} else if len(a1) > len(a2) {
				return 1
			}
			return 0

		case TypeObj:
			o1 := v1.(map[string]interface{})
			o2 := v2.(map[string]interface{})
			switch {
			case len(o1) < len(o2):
				return -1
			case len(o1) > len(o2):
				return 1
			}

			keys1 := make([]string, 0)
			for key := range o1 {
				keys1 = append(keys1, key)
			}
			sort.Strings(keys1)
			keys2 := make([]string, 0)
			for key := range o2 {
				keys2 = append(keys2, key)
			}
			sort.Strings(keys2)

			for i, key1 := range keys1 {
				key2 := keys2[i]
				val1, val2 := o1[key1], o2[key2]
				switch {
				case key1 < key2:
					return -1
				case key1 > key2:
					return 1
				case key1 == key2:
					cmp := valuecompare(val1, val2)
					if cmp != 0 {
						return cmp
					}
				}
			}
			return 0
		}
	}
	panic(fmt.Errorf("unknown value type: %T", v1))
}

func valueType(data interface{}) byte {
	if data == nil {
		return TypeNull
	}
	switch v := data.(type) {
	case bool:
		if v == false {
			return TypeFalse
		}
		return TypeTrue
	case float64:
		return TypeNumber
	case int64, uint64, int, uint:
		return TypeNumber
	case string:
		return TypeString
	case []interface{}:
		return TypeArray
	case map[string]interface{}:
		return TypeObj
	}
	panic(fmt.Errorf("unknown value type: %T", data))
}

func cmpnumber(n1, n2 interface{}) int {
	switch v1 := n1.(type) {
	case float64:
		return float64t(v1).cmp(n2)
	case int:
		return int64t(v1).cmp(n2)
	case int64:
		return int64t(v1).cmp(n2)
	case uint:
		return uint64t(v1).cmp(n2)
	case uint64:
		return uint64t(v1).cmp(n2)
	}
	panic(fmt.Errorf("unknown value type: %T", n1))
}

type float64t float64

func (this float64t) cmp(other interface{}) int {
	var rc int
	switch {
	case float64(this) < float64(int64(this)):
		rc = -1
	case float64(this) > float64(int64(this)):
		rc = 1
	}

	switch val := other.(type) {
	case float64:
		if float64(this) < val {
			return -1
		} else if float64(this) == val {
			return 0
		}
		return 1

	case int64:
		if v := int64(this); v < val {
			return -1
		} else if v == val {
			return rc
		}
		return 1

	case uint64:
		if v := uint64(this); v < val {
			return -1
		} else if v == val {
			return rc
		}
		return 1
	}
	panic(fmt.Errorf("cannot handle type %T", other))
}

type int64t int64

func (this int64t) cmp(other interface{}) int {
	switch val := other.(type) {
	case float64:
		return -float64t(val).cmp(int64(this))
	case int:
		if int64(this) < int64(val) {
			return -1
		} else if int64(this) == int64(val) {
			return 0
		}
		return 1

	case int64:
		if int64(this) < val {
			return -1
		} else if int64(this) == val {
			return 0
		}
		return 1

	case uint64:
		if val >= 9223372036854775808 {
			return -1
		} else if v := int64(val); this < int64t(v) {
			return -1
		} else if this == int64t(v) {
			return 0
		}
		return 1
	}
	panic(fmt.Errorf("cannot handle type %T", other))
}

type uint64t int64

func (this uint64t) cmp(other interface{}) int {
	switch val := other.(type) {
	case float64:
		return -float64t(val).cmp(uint64(this))

	case int64:
		if val < 0 {
			return 1
		} else if v := int64(val); int64(this) < v {
			return -1
		} else if int64(this) == v {
			return 0
		}
		return 1

	case uint64:
		if uint64(this) < val {
			return -1
		} else if uint64(this) == val {
			return 0
		}
		return 1
	}
	panic(fmt.Errorf("cannot handle type %T", other))
}
<|endoftext|>


<commit_before>package template

import (
	"io"
	"reflect"
	"strconv"
)

// If v is a string, put it in single quotes.
// Otherwise return the string normally.
func quoteString(v Value) string {
	if s, ok := v.(stringValue); ok {
		return "'" + string(s) + "'"
	}
	return v.String()
}

// TODO: should there be a Float method on Value?

// A Value represents a generic value of any type.
type Value interface {
	Node

	// Bool coerces the Value to a boolean. Values that are true are:
	//	- A bool that is true
	//	- A non-zero integer
	//	- A non-empty string
	//	- A slice, array, map, or channel with non-zero length
	//	- Any struct
	//	- A non-nil pointer to any of the above
	// All other values evaluate to false.
	Bool() bool

	// Int coerces the Value to a signed integer. The return parameter follows these
	// rules:
	//	- A bool that is true evaluates to 1; false evaluates to 0
	//	- An integer evaluates to itself, with unsigned types possibly overflowing
	//	- A string is converted to a signed integer if possible
	//	- A non-nil pointer to one of the above types uses
	// All other values evaluate to 0.
	Int() int64

	// String coerces the Value to a string. The return parameter follows these rules:
	//	- A bool returns "true" if it is true and "false" otherwise
	//	- An integer is converted to its string representation
	//	- A string evaluates to itself
	//	- A non-nil pointer evaluates to whatever its element would evaluate to
	//	according to these rules
	// All other values evaluate to the empty string "".
	String() string

	// Uint coerces the Value to an unsigned integer. It uses the same rules as
	// Int except that negative signed integers will underflow to positive integers.
	Uint() uint64

	// Reflect returns the Value's reflected value.
	Reflect() reflect.Value
}

type nilValue byte

func (n nilValue) Bool() bool                      { return false }
func (n nilValue) Int() int64                      { return 0 }
func (n nilValue) String() string                  { return "" }
func (n nilValue) Uint() uint64                    { return 0 }
func (n nilValue) Reflect() reflect.Value          { return reflect.ValueOf(nil) }
func (n nilValue) Render(wr io.Writer, c *Context) {}

type boolValue bool

func (b boolValue) Bool() bool { return bool(b) }

func (b boolValue) Int() int64 {
	if b {
		return 1
	}
	return 0
}

func (b boolValue) String() string {
	if b {
		return "true"
	}
	return "false"
}

func (b boolValue) Uint() uint64 {
	if b {
		return 1
	}
	return 0
}

func (b boolValue) Reflect() reflect.Value          { return reflect.ValueOf(b) }
func (b boolValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(b.String())) }

type stringValue string

func (str stringValue) Bool() bool { return str != "" }

func (str stringValue) Int() int64 {
	if i, err := strconv.Atoi64(string(str)); err == nil {
		return i
	}
	return 0
}

func (str stringValue) String() string { return string(str) }

func (str stringValue) Uint() uint64 {
	if i, err := strconv.Atoui64(string(str)); err == nil {
		return i
	}
	return 0
}

func (str stringValue) Reflect() reflect.Value          { return reflect.ValueOf(str) }
func (str stringValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(string(str))) }

type intValue int64

func (i intValue) Bool() bool                      { return i != 0 }
func (i intValue) Int() int64                      { return int64(i) }
func (i intValue) String() string                  { return strconv.Itoa64(int64(i)) }
func (i intValue) Uint() uint64                    { return uint64(i) }
func (i intValue) Reflect() reflect.Value          { return reflect.ValueOf(i) }
func (i intValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(i.String())) }

type floatValue float64

func (f floatValue) Bool() bool             { return f != 0 }
func (f floatValue) Int() int64             { return int64(f) }
func (f floatValue) String() string         { return strconv.Ftoa64(float64(f), 'g', -1) }
func (f floatValue) Uint() uint64           { return uint64(f) }
func (f floatValue) Reflect() reflect.Value { return reflect.ValueOf(f) }

func (f floatValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(f.String())) }

type complexValue complex128

func (c complexValue) Bool() bool { return c != 0 }
func (c complexValue) Int() int64 { return 0 }

func (c complexValue) String() string {
	a, b := real(c), imag(c)
	return strconv.Ftoa64(a, 'g', -1) + "+" + strconv.Ftoa64(b, 'g', -1) + "i"
}

func (c complexValue) Uint() uint64           { return 0 }
func (c complexValue) Reflect() reflect.Value { return reflect.ValueOf(c) }

func (c complexValue) Render(wr io.Writer, _ *Context) {
	wr.Write([]byte(c.String()))
}

// reflectValue implements the common Value methods for reflected types.
type reflectValue reflect.Value

func (v reflectValue) Bool() bool             { return reflect.Value(v).Len() != 0 }
func (v reflectValue) Int() int64             { return 0 }
func (v reflectValue) Uint() uint64           { return 0 }
func (v reflectValue) Reflect() reflect.Value { return reflect.Value(v) }

// arrayValue represents a slice or array value
type arrayValue struct {
	reflectValue
}

func (a arrayValue) String() string {
	v := reflect.Value(a.reflectValue)
	str := "["
	for i := 0; i < v.Len(); i++ {
		if i > 0 {
			str += ", "
		}
		str1 := quoteString(refToVal(v.Index(i)))
		str += str1
	}
	str += "]"
	return str
}
func (a arrayValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(a.String())) }

type mapValue struct {
	reflectValue
}

func (m mapValue) String() string {
	v := reflect.Value(m.reflectValue)
	keys := v.MapKeys()
	str := "{"
	for i, key := range keys {
		if i > 0 {
			str += ", "
		}
		v1 := refToVal(key)
		str1 := quoteString(v1)
		str += str1
		str += ": "
		v1 = refToVal(v.MapIndex(key))
		str1 = quoteString(v1)
		str += str1
	}
	str += "}"
	return str
}
func (m mapValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(m.String())) }

type chanValue struct {
	reflectValue
}

func (ch chanValue) String() string {
	// TODO: implement
	return ""
}
func (ch chanValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(ch.String())) }

type structValue struct {
	reflectValue
}

func (st structValue) Bool() bool { return true }
func (st structValue) String() string {
	// TODO: implement
	return ""
}
func (st structValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(st.String())) }

type pointerValue struct {
	reflectValue
}

func (p pointerValue) value() Value { return refToVal(reflect.Value(p.reflectValue).Elem()) }
// TODO: correct
func (p pointerValue) Bool() bool { return !reflect.Value(p.reflectValue).IsNil() }
func (p pointerValue) String() string {
	if reflect.Value(p.reflectValue).IsNil() {
		return "<nil>"
	}
	return p.value().String()
}
func (p pointerValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(p.String())) }

// A Variable is an index into a Context's stack.
// Variables must be obtained through the Parser before runtime.
type Variable int

func (v Variable) Eval(c *Context) Value {
	if val := c.stack[v]; val != nil {
		return val
	}
	return nilValue(0)
}

func (v Variable) Set(val Value, c *Context) { c.stack[v] = val }

func refToVal(ref reflect.Value) Value {
	switch ref.Kind() {
	case reflect.Bool:
		return boolValue(ref.Bool())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intValue(ref.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
		reflect.Uint64, reflect.Uintptr:
		return intValue(ref.Uint())
	case reflect.Float32, reflect.Float64:
		return floatValue(ref.Float())
	case reflect.Complex64, reflect.Complex128:
		return complexValue(ref.Complex())
	case reflect.Array, reflect.Slice:
		return arrayValue{reflectValue(ref)}
	case reflect.Chan:
		return chanValue{reflectValue(ref)}
	case reflect.Map:
		return mapValue{reflectValue(ref)}
	case reflect.Ptr:
		return pointerValue{reflectValue(ref)}
	case reflect.String:
		return stringValue(ref.String())
	case reflect.Struct:
		return structValue{reflectValue(ref)}
	}
	return nilValue(0)
}

func lookup(v reflect.Value, s string) reflect.Value {
	var ret reflect.Value
	v = reflect.Indirect(v)
	switch v.Kind() {
	case reflect.Array, reflect.Slice:
		if idx, err := strconv.Atoi(s); err == nil {
			ret = v.Index(idx)
		}
	case reflect.Map:
		keyt := v.Type().Key()
		switch keyt.Kind() {
		case reflect.String:
			ret = v.MapIndex(reflect.ValueOf(s))
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if idx, err := strconv.Atoi64(s); err == nil {
				idxVal := reflect.New(keyt).Elem()
				idxVal.SetInt(idx)
				ret = v.MapIndex(idxVal)
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			if idx, err := strconv.Atoui64(s); err == nil {
				idxVal := reflect.New(keyt).Elem()
				idxVal.SetUint(idx)
				ret = v.MapIndex(idxVal)
			}
		}
	case reflect.Struct:
		ret = v.FieldByName(s)
	}
	// TODO: Find a way to look up methods by name
	return ret
}
<commit_msg>Use io.WriteString where possible<commit_after>package template

import (
	"io"
	"reflect"
	"strconv"
)

// If v is a string, put it in single quotes.
// Otherwise return the string normally.
func quoteString(v Value) string {
	if s, ok := v.(stringValue); ok {
		return "'" + string(s) + "'"
	}
	return v.String()
}

// TODO: should there be a Float method on Value?

// A Value represents a generic value of any type.
type Value interface {
	Node

	// Bool coerces the Value to a boolean. Values that are true are:
	//	- A bool that is true
	//	- A non-zero integer
	//	- A non-empty string
	//	- A slice, array, map, or channel with non-zero length
	//	- Any struct
	//	- A non-nil pointer to any of the above
	// All other values evaluate to false.
	Bool() bool

	// Int coerces the Value to a signed integer. The return parameter follows these
	// rules:
	//	- A bool that is true evaluates to 1; false evaluates to 0
	//	- An integer evaluates to itself, with unsigned types possibly overflowing
	//	- A string is converted to a signed integer if possible
	//	- A non-nil pointer to one of the above types uses
	// All other values evaluate to 0.
	Int() int64

	// String coerces the Value to a string. The return parameter follows these rules:
	//	- A bool returns "true" if it is true and "false" otherwise
	//	- An integer is converted to its string representation
	//	- A string evaluates to itself
	//	- A non-nil pointer evaluates to whatever its element would evaluate to
	//	according to these rules
	// All other values evaluate to the empty string "".
	String() string

	// Uint coerces the Value to an unsigned integer. It uses the same rules as
	// Int except that negative signed integers will underflow to positive integers.
	Uint() uint64

	// Reflect returns the Value's reflected value.
	Reflect() reflect.Value
}

type nilValue byte

func (n nilValue) Bool() bool                      { return false }
func (n nilValue) Int() int64                      { return 0 }
func (n nilValue) String() string                  { return "" }
func (n nilValue) Uint() uint64                    { return 0 }
func (n nilValue) Reflect() reflect.Value          { return reflect.ValueOf(nil) }
func (n nilValue) Render(wr io.Writer, c *Context) {}

type boolValue bool

func (b boolValue) Bool() bool { return bool(b) }

func (b boolValue) Int() int64 {
	if b {
		return 1
	}
	return 0
}

func (b boolValue) String() string {
	if b {
		return "true"
	}
	return "false"
}

func (b boolValue) Uint() uint64 {
	if b {
		return 1
	}
	return 0
}

func (b boolValue) Reflect() reflect.Value          { return reflect.ValueOf(b) }
func (b boolValue) Render(w io.Writer, c *Context) { io.WriteString(w, b.String()) }

type stringValue string

func (str stringValue) Bool() bool { return str != "" }

func (str stringValue) Int() int64 {
	if i, err := strconv.Atoi64(string(str)); err == nil {
		return i
	}
	return 0
}

func (str stringValue) String() string { return string(str) }

func (str stringValue) Uint() uint64 {
	if i, err := strconv.Atoui64(string(str)); err == nil {
		return i
	}
	return 0
}

func (str stringValue) Reflect() reflect.Value          { return reflect.ValueOf(str) }
func (str stringValue) Render(w io.Writer, c *Context) { io.WriteString(w, string(str)) }

type intValue int64

func (i intValue) Bool() bool                      { return i != 0 }
func (i intValue) Int() int64                      { return int64(i) }
func (i intValue) String() string                  { return strconv.Itoa64(int64(i)) }
func (i intValue) Uint() uint64                    { return uint64(i) }
func (i intValue) Reflect() reflect.Value          { return reflect.ValueOf(i) }
func (i intValue) Render(w io.Writer, c *Context) { io.WriteString(w, i.String()) }

type floatValue float64

func (f floatValue) Bool() bool             { return f != 0 }
func (f floatValue) Int() int64             { return int64(f) }
func (f floatValue) String() string         { return strconv.Ftoa64(float64(f), 'g', -1) }
func (f floatValue) Uint() uint64           { return uint64(f) }
func (f floatValue) Reflect() reflect.Value { return reflect.ValueOf(f) }

func (f floatValue) Render(w io.Writer, c *Context) { io.WriteString(w, f.String()) }

type complexValue complex128

func (c complexValue) Bool() bool { return c != 0 }
func (c complexValue) Int() int64 { return 0 }

func (c complexValue) String() string {
	a, b := real(c), imag(c)
	return strconv.Ftoa64(a, 'g', -1) + "+" + strconv.Ftoa64(b, 'g', -1) + "i"
}

func (c complexValue) Uint() uint64           { return 0 }
func (c complexValue) Reflect() reflect.Value { return reflect.ValueOf(c) }

func (c complexValue) Render(w io.Writer, _ *Context) {
	io.WriteString(w, c.String())
}

// reflectValue implements the common Value methods for reflected types.
type reflectValue reflect.Value

func (v reflectValue) Bool() bool             { return reflect.Value(v).Len() != 0 }
func (v reflectValue) Int() int64             { return 0 }
func (v reflectValue) Uint() uint64           { return 0 }
func (v reflectValue) Reflect() reflect.Value { return reflect.Value(v) }

// arrayValue represents a slice or array value
type arrayValue struct {
	reflectValue
}

func (a arrayValue) String() string {
	v := reflect.Value(a.reflectValue)
	str := "["
	for i := 0; i < v.Len(); i++ {
		if i > 0 {
			str += ", "
		}
		str1 := quoteString(refToVal(v.Index(i)))
		str += str1
	}
	str += "]"
	return str
}
func (a arrayValue) Render(w io.Writer, c *Context) { io.WriteString(w, a.String()) }

type mapValue struct {
	reflectValue
}

func (m mapValue) String() string {
	v := reflect.Value(m.reflectValue)
	keys := v.MapKeys()
	str := "{"
	for i, key := range keys {
		if i > 0 {
			str += ", "
		}
		v1 := refToVal(key)
		str1 := quoteString(v1)
		str += str1
		str += ": "
		v1 = refToVal(v.MapIndex(key))
		str1 = quoteString(v1)
		str += str1
	}
	str += "}"
	return str
}
func (m mapValue) Render(w io.Writer, c *Context) { io.WriteString(w, m.String()) }

type chanValue struct {
	reflectValue
}

func (ch chanValue) String() string {
	// TODO: implement
	return ""
}
func (ch chanValue) Render(w io.Writer, c *Context) { io.WriteString(w, ch.String()) }

type structValue struct {
	reflectValue
}

func (st structValue) Bool() bool { return true }
func (st structValue) String() string {
	// TODO: implement
	return ""
}
func (st structValue) Render(w io.Writer, c *Context) { io.WriteString(w, st.String()) }

type pointerValue struct {
	reflectValue
}

func (p pointerValue) value() Value { return refToVal(reflect.Value(p.reflectValue).Elem()) }
// TODO: correct
func (p pointerValue) Bool() bool { return !reflect.Value(p.reflectValue).IsNil() }
func (p pointerValue) String() string {
	if reflect.Value(p.reflectValue).IsNil() {
		return "<nil>"
	}
	return p.value().String()
}
func (p pointerValue) Render(w io.Writer, c *Context) { io.WriteString(w, p.String()) }

// A Variable is an index into a Context's stack.
// Variables must be obtained through the Parser before runtime.
type Variable int

func (v Variable) Eval(c *Context) Value {
	if val := c.stack[v]; val != nil {
		return val
	}
	return nilValue(0)
}

func (v Variable) Set(val Value, c *Context) { c.stack[v] = val }

func refToVal(ref reflect.Value) Value {
	switch ref.Kind() {
	case reflect.Bool:
		return boolValue(ref.Bool())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intValue(ref.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
		reflect.Uint64, reflect.Uintptr:
		return intValue(ref.Uint())
	case reflect.Float32, reflect.Float64:
		return floatValue(ref.Float())
	case reflect.Complex64, reflect.Complex128:
		return complexValue(ref.Complex())
	case reflect.Array, reflect.Slice:
		return arrayValue{reflectValue(ref)}
	case reflect.Chan:
		return chanValue{reflectValue(ref)}
	case reflect.Map:
		return mapValue{reflectValue(ref)}
	case reflect.Ptr:
		return pointerValue{reflectValue(ref)}
	case reflect.String:
		return stringValue(ref.String())
	case reflect.Struct:
		return structValue{reflectValue(ref)}
	}
	return nilValue(0)
}

func lookup(v reflect.Value, s string) reflect.Value {
	var ret reflect.Value
	v = reflect.Indirect(v)
	switch v.Kind() {
	case reflect.Array, reflect.Slice:
		if idx, err := strconv.Atoi(s); err == nil {
			ret = v.Index(idx)
		}
	case reflect.Map:
		keyt := v.Type().Key()
		switch keyt.Kind() {
		case reflect.String:
			ret = v.MapIndex(reflect.ValueOf(s))
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if idx, err := strconv.Atoi64(s); err == nil {
				idxVal := reflect.New(keyt).Elem()
				idxVal.SetInt(idx)
				ret = v.MapIndex(idxVal)
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			if idx, err := strconv.Atoui64(s); err == nil {
				idxVal := reflect.New(keyt).Elem()
				idxVal.SetUint(idx)
				ret = v.MapIndex(idxVal)
			}
		}
	case reflect.Struct:
		ret = v.FieldByName(s)
	}
	// TODO: Find a way to look up methods by name
	return ret
}
<|endoftext|>


<commit_before>package apps

import (
	"encoding/json"
	"io"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/permissions"
)

// KonnManifest contains all the informations associated with an installed
// konnector.
type KonnManifest struct {
	DocRev string `json:"_rev,omitempty"`

	Name       string `json:"name"`
	NamePrefix string `json:"name_prefix,omitempty"`
	Editor     string `json:"editor"`
	Icon       string `json:"icon"`

	Type        string           `json:"type,omitempty"`
	License     string           `json:"license,omitempty"`
	Language    string           `json:"language,omitempty"`
	VendorLink  string           `json:"vendor_link"`
	Locales     *json.RawMessage `json:"locales,omitempty"`
	Langs       *json.RawMessage `json:"langs,omitempty"`
	Platforms   *json.RawMessage `json:"platforms,omitempty"`
	Categories  *json.RawMessage `json:"categories,omitempty"`
	Developer   *json.RawMessage `json:"developer,omitempty"`
	Screenshots *json.RawMessage `json:"screenshots,omitempty"`
	Tags        *json.RawMessage `json:"tags,omitempty"`

	Frequency    string           `json:"frequency"`
	DataType     *json.RawMessage `json:"data_type"`
	Doctypes     *json.RawMessage `json:"doctypes"`
	Fields       *json.RawMessage `json:"fields"`
	Messages     *json.RawMessage `json:"messages"`
	OAuth        *json.RawMessage `json:"oauth"`
	TimeInterval *json.RawMessage `json:"time_interval"`

	Parameters    *json.RawMessage `json:"parameters,omitempty"`
	Notifications Notifications    `json:"notifications"`

	// OnDeleteAccount can be used to specify a file path which will be executed
	// when an account associated with the konnector is deleted.
	OnDeleteAccount string `json:"on_delete_account,omitempty"`

	DocSlug        string          `json:"slug"`
	DocState       State           `json:"state"`
	DocSource      string          `json:"source"`
	DocVersion     string          `json:"version"`
	DocPermissions permissions.Set `json:"permissions"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`

	Err string `json:"error,omitempty"`
	err error
}

// ID is part of the Manifest interface
func (m *KonnManifest) ID() string { return m.DocType() + "/" + m.DocSlug }

// Rev is part of the Manifest interface
func (m *KonnManifest) Rev() string { return m.DocRev }

// DocType is part of the Manifest interface
func (m *KonnManifest) DocType() string { return consts.Konnectors }

// Clone is part of the Manifest interface
func (m *KonnManifest) Clone() couchdb.Doc {
	cloned := *m

	cloned.DocPermissions = make(permissions.Set, len(m.DocPermissions))
	copy(cloned.DocPermissions, m.DocPermissions)

	cloned.Locales = cloneRawMessage(m.Locales)
	cloned.Langs = cloneRawMessage(m.Langs)
	cloned.Platforms = cloneRawMessage(m.Platforms)
	cloned.Categories = cloneRawMessage(m.Categories)
	cloned.Developer = cloneRawMessage(m.Developer)
	cloned.Screenshots = cloneRawMessage(m.Screenshots)
	cloned.Tags = cloneRawMessage(m.Tags)
	cloned.Parameters = cloneRawMessage(m.Parameters)

	cloned.DataType = cloneRawMessage(m.DataType)
	cloned.Doctypes = cloneRawMessage(m.Doctypes)
	cloned.Fields = cloneRawMessage(m.Fields)
	cloned.Messages = cloneRawMessage(m.Messages)
	cloned.OAuth = cloneRawMessage(m.OAuth)
	cloned.TimeInterval = cloneRawMessage(m.TimeInterval)
	return &cloned
}

// SetID is part of the Manifest interface
func (m *KonnManifest) SetID(id string) {}

// SetRev is part of the Manifest interface
func (m *KonnManifest) SetRev(rev string) { m.DocRev = rev }

// Source is part of the Manifest interface
func (m *KonnManifest) Source() string { return m.DocSource }

// Version is part of the Manifest interface
func (m *KonnManifest) Version() string { return m.DocVersion }

// Slug is part of the Manifest interface
func (m *KonnManifest) Slug() string { return m.DocSlug }

// State is part of the Manifest interface
func (m *KonnManifest) State() State { return m.DocState }

// LastUpdate is part of the Manifest interface
func (m *KonnManifest) LastUpdate() time.Time { return m.UpdatedAt }

// SetState is part of the Manifest interface
func (m *KonnManifest) SetState(state State) { m.DocState = state }

// SetVersion is part of the Manifest interface
func (m *KonnManifest) SetVersion(version string) { m.DocVersion = version }

// AppType is part of the Manifest interface
func (m *KonnManifest) AppType() AppType { return Konnector }

// Permissions is part of the Manifest interface
func (m *KonnManifest) Permissions() permissions.Set {
	return m.DocPermissions
}

// SetError is part of the Manifest interface
func (m *KonnManifest) SetError(err error) {
	m.SetState(Errored)
	m.Err = err.Error()
	m.err = err
}

// Error is part of the Manifest interface
func (m *KonnManifest) Error() error { return m.err }

// Match is part of the Manifest interface
func (m *KonnManifest) Match(field, value string) bool {
	switch field {
	case "slug":
		return m.DocSlug == value
	case "state":
		return m.DocState == State(value)
	}
	return false
}

// ReadManifest is part of the Manifest interface
func (m *KonnManifest) ReadManifest(r io.Reader, slug, sourceURL string) error {
	var newManifest KonnManifest
	if err := json.NewDecoder(r).Decode(&newManifest); err != nil {
		return ErrBadManifest
	}

	newManifest.SetID(m.ID())
	newManifest.SetRev(m.Rev())
	newManifest.SetState(m.State())
	newManifest.CreatedAt = m.CreatedAt
	newManifest.DocSlug = slug
	newManifest.DocSource = sourceURL
	newManifest.Parameters = m.Parameters

	*m = newManifest
	return nil
}

// Create is part of the Manifest interface
func (m *KonnManifest) Create(db couchdb.Database) error {
	m.CreatedAt = time.Now()
	m.UpdatedAt = time.Now()
	if err := couchdb.CreateNamedDocWithDB(db, m); err != nil {
		return err
	}
	_, err := permissions.CreateKonnectorSet(db, m.Slug(), m.Permissions())
	return err
}

// Update is part of the Manifest interface
func (m *KonnManifest) Update(db couchdb.Database) error {
	m.UpdatedAt = time.Now()
	err := couchdb.UpdateDoc(db, m)
	if err != nil {
		return err
	}
	_, err = permissions.UpdateKonnectorSet(db, m.Slug(), m.Permissions())
	return err
}

// Delete is part of the Manifest interface
func (m *KonnManifest) Delete(db couchdb.Database) error {
	err := permissions.DestroyKonnector(db, m.Slug())
	if err != nil && !couchdb.IsNotFoundError(err) {
		return err
	}
	return couchdb.DeleteDoc(db, m)
}

// GetKonnectorBySlug fetch the manifest of a konnector from the database given
// a slug.
func GetKonnectorBySlug(db couchdb.Database, slug string) (*KonnManifest, error) {
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}
	man := &KonnManifest{}
	err := couchdb.GetDoc(db, consts.Konnectors, consts.Konnectors+"/"+slug, man)
	if couchdb.IsNotFoundError(err) {
		return nil, ErrNotFound
	}
	if err != nil {
		return nil, err
	}
	return man, nil
}

// ListKonnectors returns the list of installed konnectors applications.
//
// TODO: pagination
func ListKonnectors(db couchdb.Database) ([]Manifest, error) {
	var docs []*KonnManifest
	req := &couchdb.AllDocsRequest{Limit: 100}
	err := couchdb.GetAllDocs(db, consts.Konnectors, req, &docs)
	if err != nil {
		return nil, err
	}
	mans := make([]Manifest, len(docs))
	for i, m := range docs {
		mans[i] = m
	}
	return mans, nil
}

var _ Manifest = &KonnManifest{}
<commit_msg>Pluralize data_type field (data_types)<commit_after>package apps

import (
	"encoding/json"
	"io"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/permissions"
)

// KonnManifest contains all the informations associated with an installed
// konnector.
type KonnManifest struct {
	DocRev string `json:"_rev,omitempty"`

	Name       string `json:"name"`
	NamePrefix string `json:"name_prefix,omitempty"`
	Editor     string `json:"editor"`
	Icon       string `json:"icon"`

	Type        string           `json:"type,omitempty"`
	License     string           `json:"license,omitempty"`
	Language    string           `json:"language,omitempty"`
	VendorLink  string           `json:"vendor_link"`
	Locales     *json.RawMessage `json:"locales,omitempty"`
	Langs       *json.RawMessage `json:"langs,omitempty"`
	Platforms   *json.RawMessage `json:"platforms,omitempty"`
	Categories  *json.RawMessage `json:"categories,omitempty"`
	Developer   *json.RawMessage `json:"developer,omitempty"`
	Screenshots *json.RawMessage `json:"screenshots,omitempty"`
	Tags        *json.RawMessage `json:"tags,omitempty"`

	Frequency    string           `json:"frequency"`
	DataTypes    *json.RawMessage `json:"data_types"`
	Doctypes     *json.RawMessage `json:"doctypes"`
	Fields       *json.RawMessage `json:"fields"`
	Messages     *json.RawMessage `json:"messages"`
	OAuth        *json.RawMessage `json:"oauth"`
	TimeInterval *json.RawMessage `json:"time_interval"`

	Parameters    *json.RawMessage `json:"parameters,omitempty"`
	Notifications Notifications    `json:"notifications"`

	// OnDeleteAccount can be used to specify a file path which will be executed
	// when an account associated with the konnector is deleted.
	OnDeleteAccount string `json:"on_delete_account,omitempty"`

	DocSlug        string          `json:"slug"`
	DocState       State           `json:"state"`
	DocSource      string          `json:"source"`
	DocVersion     string          `json:"version"`
	DocPermissions permissions.Set `json:"permissions"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`

	Err string `json:"error,omitempty"`
	err error
}

// ID is part of the Manifest interface
func (m *KonnManifest) ID() string { return m.DocType() + "/" + m.DocSlug }

// Rev is part of the Manifest interface
func (m *KonnManifest) Rev() string { return m.DocRev }

// DocType is part of the Manifest interface
func (m *KonnManifest) DocType() string { return consts.Konnectors }

// Clone is part of the Manifest interface
func (m *KonnManifest) Clone() couchdb.Doc {
	cloned := *m

	cloned.DocPermissions = make(permissions.Set, len(m.DocPermissions))
	copy(cloned.DocPermissions, m.DocPermissions)

	cloned.Locales = cloneRawMessage(m.Locales)
	cloned.Langs = cloneRawMessage(m.Langs)
	cloned.Platforms = cloneRawMessage(m.Platforms)
	cloned.Categories = cloneRawMessage(m.Categories)
	cloned.Developer = cloneRawMessage(m.Developer)
	cloned.Screenshots = cloneRawMessage(m.Screenshots)
	cloned.Tags = cloneRawMessage(m.Tags)
	cloned.Parameters = cloneRawMessage(m.Parameters)

	cloned.DataTypes = cloneRawMessage(m.DataTypes)
	cloned.Doctypes = cloneRawMessage(m.Doctypes)
	cloned.Fields = cloneRawMessage(m.Fields)
	cloned.Messages = cloneRawMessage(m.Messages)
	cloned.OAuth = cloneRawMessage(m.OAuth)
	cloned.TimeInterval = cloneRawMessage(m.TimeInterval)
	return &cloned
}

// SetID is part of the Manifest interface
func (m *KonnManifest) SetID(id string) {}

// SetRev is part of the Manifest interface
func (m *KonnManifest) SetRev(rev string) { m.DocRev = rev }

// Source is part of the Manifest interface
func (m *KonnManifest) Source() string { return m.DocSource }

// Version is part of the Manifest interface
func (m *KonnManifest) Version() string { return m.DocVersion }

// Slug is part of the Manifest interface
func (m *KonnManifest) Slug() string { return m.DocSlug }

// State is part of the Manifest interface
func (m *KonnManifest) State() State { return m.DocState }

// LastUpdate is part of the Manifest interface
func (m *KonnManifest) LastUpdate() time.Time { return m.UpdatedAt }

// SetState is part of the Manifest interface
func (m *KonnManifest) SetState(state State) { m.DocState = state }

// SetVersion is part of the Manifest interface
func (m *KonnManifest) SetVersion(version string) { m.DocVersion = version }

// AppType is part of the Manifest interface
func (m *KonnManifest) AppType() AppType { return Konnector }

// Permissions is part of the Manifest interface
func (m *KonnManifest) Permissions() permissions.Set {
	return m.DocPermissions
}

// SetError is part of the Manifest interface
func (m *KonnManifest) SetError(err error) {
	m.SetState(Errored)
	m.Err = err.Error()
	m.err = err
}

// Error is part of the Manifest interface
func (m *KonnManifest) Error() error { return m.err }

// Match is part of the Manifest interface
func (m *KonnManifest) Match(field, value string) bool {
	switch field {
	case "slug":
		return m.DocSlug == value
	case "state":
		return m.DocState == State(value)
	}
	return false
}

// ReadManifest is part of the Manifest interface
func (m *KonnManifest) ReadManifest(r io.Reader, slug, sourceURL string) error {
	var newManifest KonnManifest
	if err := json.NewDecoder(r).Decode(&newManifest); err != nil {
		return ErrBadManifest
	}

	newManifest.SetID(m.ID())
	newManifest.SetRev(m.Rev())
	newManifest.SetState(m.State())
	newManifest.CreatedAt = m.CreatedAt
	newManifest.DocSlug = slug
	newManifest.DocSource = sourceURL
	newManifest.Parameters = m.Parameters

	*m = newManifest
	return nil
}

// Create is part of the Manifest interface
func (m *KonnManifest) Create(db couchdb.Database) error {
	m.CreatedAt = time.Now()
	m.UpdatedAt = time.Now()
	if err := couchdb.CreateNamedDocWithDB(db, m); err != nil {
		return err
	}
	_, err := permissions.CreateKonnectorSet(db, m.Slug(), m.Permissions())
	return err
}

// Update is part of the Manifest interface
func (m *KonnManifest) Update(db couchdb.Database) error {
	m.UpdatedAt = time.Now()
	err := couchdb.UpdateDoc(db, m)
	if err != nil {
		return err
	}
	_, err = permissions.UpdateKonnectorSet(db, m.Slug(), m.Permissions())
	return err
}

// Delete is part of the Manifest interface
func (m *KonnManifest) Delete(db couchdb.Database) error {
	err := permissions.DestroyKonnector(db, m.Slug())
	if err != nil && !couchdb.IsNotFoundError(err) {
		return err
	}
	return couchdb.DeleteDoc(db, m)
}

// GetKonnectorBySlug fetch the manifest of a konnector from the database given
// a slug.
func GetKonnectorBySlug(db couchdb.Database, slug string) (*KonnManifest, error) {
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}
	man := &KonnManifest{}
	err := couchdb.GetDoc(db, consts.Konnectors, consts.Konnectors+"/"+slug, man)
	if couchdb.IsNotFoundError(err) {
		return nil, ErrNotFound
	}
	if err != nil {
		return nil, err
	}
	return man, nil
}

// ListKonnectors returns the list of installed konnectors applications.
//
// TODO: pagination
func ListKonnectors(db couchdb.Database) ([]Manifest, error) {
	var docs []*KonnManifest
	req := &couchdb.AllDocsRequest{Limit: 100}
	err := couchdb.GetAllDocs(db, consts.Konnectors, req, &docs)
	if err != nil {
		return nil, err
	}
	mans := make([]Manifest, len(docs))
	for i, m := range docs {
		mans[i] = m
	}
	return mans, nil
}

var _ Manifest = &KonnManifest{}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"time"

	"github.com/01org/ciao/ciao-controller/types"
	"github.com/01org/ciao/payloads"
	"github.com/01org/ciao/ssntp/uuid"
	"github.com/golang/glog"
	"gopkg.in/yaml.v2"
)

type config struct {
	sc     payloads.Start
	config string
	cnci   bool
	mac    string
	ip     string
}

type instance struct {
	types.Instance
	newConfig config
	ctl       *controller
	startTime time.Time
}

func isCNCIWorkload(workload *types.Workload) bool {
	for r := range workload.Defaults {
		if workload.Defaults[r].Type == payloads.NetworkNode {
			return true
		}
	}

	return false
}

func newInstance(ctl *controller, tenantID string, workload *types.Workload) (*instance, error) {
	id := uuid.Generate()

	config, err := newConfig(ctl, workload, id.String(), tenantID)
	if err != nil {
		return nil, err
	}

	usage := config.GetResources()

	newInstance := types.Instance{
		TenantID:   tenantID,
		WorkloadID: workload.ID,
		State:      payloads.Pending,
		ID:         id.String(),
		CNCI:       config.cnci,
		IPAddress:  config.ip,
		MACAddress: config.mac,
		Usage:      usage,
		CreateTime: time.Now(),
	}

	i := &instance{
		ctl:       ctl,
		newConfig: config,
		Instance:  newInstance,
	}

	return i, nil
}

func (i *instance) Add() error {
	if i.CNCI == false {
		ds := i.ctl.ds
		ds.AddInstance(&i.Instance)
		storage := i.newConfig.sc.Start.Storage
		if (storage != payloads.StorageResources{}) {
			_, err := ds.CreateStorageAttachment(i.Instance.ID, storage.ID, storage.Ephemeral)
			if err != nil {
				glog.Error(err)
			}
		}
	} else {
		i.ctl.ds.AddTenantCNCI(i.TenantID, i.ID, i.MACAddress)
	}

	return nil
}

func (i *instance) Clean() error {
	if i.CNCI == false {
		i.ctl.ds.ReleaseTenantIP(i.TenantID, i.IPAddress)
	}

	return nil
}

func (i *instance) Allowed() (bool, error) {
	if i.CNCI == true {
		// should I bother to check the tenant id exists?
		return true, nil
	}

	ds := i.ctl.ds

	tenant, err := ds.GetTenant(i.TenantID)
	if err != nil {
		return false, err
	}

	for _, res := range tenant.Resources {
		// check instance count separately
		if res.Rtype == 1 {
			if res.OverLimit(1) {
				return false, nil
			}
			continue
		}
		if res.OverLimit(i.Usage[res.Rname]) {
			return false, nil
		}
	}

	return true, nil
}

func (c *config) GetResources() map[string]int {
	rr := c.sc.Start.RequestedResources

	// convert RequestedResources into a map[string]int
	resources := make(map[string]int)
	for i := range rr {
		resources[string(rr[i].Type)] = rr[i].Value
	}

	return resources
}

func getStorage(c *controller, wl *types.Workload, tenant string, instanceID string) (payloads.StorageResources, error) {
	s := wl.Storage

	// storage already exists, use preexisting definition.
	if s.ID != "" {
		return payloads.StorageResources{ID: s.ID, Bootable: s.Bootable}, nil
	}

	// new storage.
	// TBD: handle all these cases
	// - create bootable volume from image.
	//   assumptions: SourceType is "image"
	//                Bootable is true
	//                SourceID points to existing image
	// - create bootable volume from volume.
	//   Assumptions: SourceType is "volume"
	//                Bootable is true
	//                SourceID points to existing volume
	// - create attachable empty volume.
	//   Assumptions: SourceType is "empty"
	//                Bootable is ignored
	//                SourceID is ignored
	// - create attachable volume from image?
	//   Assumptions: SourceType is "image"
	//                Bootable is false
	//                SourceID points to existing image
	// - create attachable volume from volume.
	//   Assumptions: SourceType is "volume"
	//                Bootable is false
	//                SourceID points to existing volume.
	// assume always persistent for now.
	// assume we have already checked quotas.
	// ID of source is the image id.
	switch s.SourceType {
	case types.ImageService:
		path, err := c.image.GetImagePath(s.SourceID)
		if err != nil {
			// this image doesn't exist
			return payloads.StorageResources{}, err
		}

		device, err := c.CreateBlockDevice(&path, s.Size)
		if err != nil {
			return payloads.StorageResources{}, err
		}

		// don't you need to add support for indicating whether
		// a block device is bootable.
		data := types.BlockData{
			BlockDevice: device,
			Size:        s.Size,
			CreateTime:  time.Now(),
			TenantID:    tenant,
			Name:        fmt.Sprintf("Storage for instance: %s", instanceID),
		}

		err = c.ds.AddBlockDevice(data)
		if err != nil {
			c.DeleteBlockDevice(device.ID)
			return payloads.StorageResources{}, err
		}

		return payloads.StorageResources{ID: data.ID, Bootable: s.Bootable, Ephemeral: true}, nil
	case types.VolumeService:
		device, err := c.CopyBlockDevice(s.SourceID)
		if err != nil {
			return payloads.StorageResources{}, err
		}

		// don't you need to add support for indicating whether
		// a block device is bootable.
		data := types.BlockData{
			BlockDevice: device,
			Size:        s.Size,
			CreateTime:  time.Now(),
			TenantID:    tenant,
			Name:        fmt.Sprintf("Storage for instance: %s", instanceID),
		}

		err = c.ds.AddBlockDevice(data)
		if err != nil {
			c.DeleteBlockDevice(device.ID)
			return payloads.StorageResources{}, err
		}

		return payloads.StorageResources{ID: data.ID, Bootable: s.Bootable}, nil

	case types.Empty:
		device, err := c.CreateBlockDevice(nil, s.Size)
		if err != nil {
			return payloads.StorageResources{}, err
		}

		// don't you need to add support for indicating whether
		// a block device is bootable.
		data := types.BlockData{
			BlockDevice: device,
			Size:        s.Size,
			CreateTime:  time.Now(),
			TenantID:    tenant,
			Name:        fmt.Sprintf("Storage for instance: %s", instanceID),
		}

		err = c.ds.AddBlockDevice(data)
		if err != nil {
			c.DeleteBlockDevice(device.ID)
			return payloads.StorageResources{}, err
		}

		return payloads.StorageResources{ID: data.ID, Bootable: false}, nil

	}

	return payloads.StorageResources{}, errors.New("Not implemented yet")
}

func newConfig(ctl *controller, wl *types.Workload, instanceID string, tenantID string) (config, error) {
	type UserData struct {
		UUID     string `json:"uuid"`
		Hostname string `json:"hostname"`
	}

	var userData UserData
	var config config

	baseConfig := wl.Config
	defaults := wl.Defaults
	imageID := wl.ImageID
	fwType := wl.FWType

	tenant, err := ctl.ds.GetTenant(tenantID)
	if err != nil {
		fmt.Println("unable to get tenant")
	}

	config.cnci = isCNCIWorkload(wl)

	var networking payloads.NetworkResources
	var storage payloads.StorageResources

	// do we ever need to save the vnic uuid?
	networking.VnicUUID = uuid.Generate().String()

	if config.cnci == false {
		ipAddress, err := ctl.ds.AllocateTenantIP(tenantID)
		if err != nil {
			fmt.Println("Unable to allocate IP address: ", err)
			return config, err
		}

		networking.VnicMAC = newTenantHardwareAddr(ipAddress).String()

		// send in CIDR notation?
		networking.PrivateIP = ipAddress.String()
		config.ip = ipAddress.String()
		mask := net.IPv4Mask(255, 255, 255, 0)
		ipnet := net.IPNet{
			IP:   ipAddress.Mask(mask),
			Mask: mask,
		}
		networking.Subnet = ipnet.String()
		networking.ConcentratorUUID = tenant.CNCIID

		// in theory we should refuse to go on if ip is null
		// for now let's keep going
		networking.ConcentratorIP = tenant.CNCIIP

		// set the hostname and uuid for userdata
		userData.UUID = instanceID
		userData.Hostname = instanceID

		// handle storage resources
		if wl.Storage != nil {
			storage, err = getStorage(ctl, wl, tenantID, instanceID)
			if err != nil {
				return config, err
			}
		}
	} else {
		networking.VnicMAC = tenant.CNCIMAC

		// set the hostname and uuid for userdata
		userData.UUID = instanceID
		userData.Hostname = "cnci-" + tenantID
	}

	// hardcode persistence until changes can be made to workload
	// template datastore.  Estimated resources can be blank
	// for now because we don't support it yet.
	startCmd := payloads.StartCmd{
		TenantUUID:          tenantID,
		InstanceUUID:        instanceID,
		ImageUUID:           imageID,
		FWType:              payloads.Firmware(fwType),
		VMType:              wl.VMType,
		InstancePersistence: payloads.Host,
		RequestedResources:  defaults,
		Networking:          networking,
		Storage:             storage,
	}

	if wl.VMType == payloads.Docker {
		startCmd.DockerImage = wl.ImageName
	}

	cmd := payloads.Start{
		Start: startCmd,
	}
	config.sc = cmd

	y, err := yaml.Marshal(&config.sc)
	if err != nil {
		glog.Warning("error marshalling config: ", err)
	}

	b, err := json.MarshalIndent(userData, "", "\t")
	if err != nil {
		glog.Warning("error marshalling user data: ", err)
	}

	config.config = "---\n" + string(y) + "...\n" + baseConfig + "---\n" + string(b) + "\n...\n"
	config.mac = networking.VnicMAC

	return config, err
}

func newTenantHardwareAddr(ip net.IP) net.HardwareAddr {
	buf := make([]byte, 6)
	ipBytes := ip.To4()
	buf[0] |= 2
	buf[1] = 0
	copy(buf[2:6], ipBytes)
	return net.HardwareAddr(buf)
}
<commit_msg>ciao-controller: Enable workload based storage for CNCI<commit_after>/*
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"time"

	"github.com/01org/ciao/ciao-controller/types"
	"github.com/01org/ciao/payloads"
	"github.com/01org/ciao/ssntp/uuid"
	"github.com/golang/glog"
	"gopkg.in/yaml.v2"
)

type config struct {
	sc     payloads.Start
	config string
	cnci   bool
	mac    string
	ip     string
}

type instance struct {
	types.Instance
	newConfig config
	ctl       *controller
	startTime time.Time
}

func isCNCIWorkload(workload *types.Workload) bool {
	for r := range workload.Defaults {
		if workload.Defaults[r].Type == payloads.NetworkNode {
			return true
		}
	}

	return false
}

func newInstance(ctl *controller, tenantID string, workload *types.Workload) (*instance, error) {
	id := uuid.Generate()

	config, err := newConfig(ctl, workload, id.String(), tenantID)
	if err != nil {
		return nil, err
	}

	usage := config.GetResources()

	newInstance := types.Instance{
		TenantID:   tenantID,
		WorkloadID: workload.ID,
		State:      payloads.Pending,
		ID:         id.String(),
		CNCI:       config.cnci,
		IPAddress:  config.ip,
		MACAddress: config.mac,
		Usage:      usage,
		CreateTime: time.Now(),
	}

	i := &instance{
		ctl:       ctl,
		newConfig: config,
		Instance:  newInstance,
	}

	return i, nil
}

func (i *instance) Add() error {
	if i.CNCI == false {
		ds := i.ctl.ds
		ds.AddInstance(&i.Instance)
		storage := i.newConfig.sc.Start.Storage
		if (storage != payloads.StorageResources{}) {
			_, err := ds.CreateStorageAttachment(i.Instance.ID, storage.ID, storage.Ephemeral)
			if err != nil {
				glog.Error(err)
			}
		}
	} else {
		i.ctl.ds.AddTenantCNCI(i.TenantID, i.ID, i.MACAddress)
	}

	return nil
}

func (i *instance) Clean() error {
	if i.CNCI == false {
		i.ctl.ds.ReleaseTenantIP(i.TenantID, i.IPAddress)
	}

	return nil
}

func (i *instance) Allowed() (bool, error) {
	if i.CNCI == true {
		// should I bother to check the tenant id exists?
		return true, nil
	}

	ds := i.ctl.ds

	tenant, err := ds.GetTenant(i.TenantID)
	if err != nil {
		return false, err
	}

	for _, res := range tenant.Resources {
		// check instance count separately
		if res.Rtype == 1 {
			if res.OverLimit(1) {
				return false, nil
			}
			continue
		}
		if res.OverLimit(i.Usage[res.Rname]) {
			return false, nil
		}
	}

	return true, nil
}

func (c *config) GetResources() map[string]int {
	rr := c.sc.Start.RequestedResources

	// convert RequestedResources into a map[string]int
	resources := make(map[string]int)
	for i := range rr {
		resources[string(rr[i].Type)] = rr[i].Value
	}

	return resources
}

func getStorage(c *controller, wl *types.Workload, tenant string, instanceID string) (payloads.StorageResources, error) {
	s := wl.Storage

	// storage already exists, use preexisting definition.
	if s.ID != "" {
		return payloads.StorageResources{ID: s.ID, Bootable: s.Bootable}, nil
	}

	// new storage.
	// TBD: handle all these cases
	// - create bootable volume from image.
	//   assumptions: SourceType is "image"
	//                Bootable is true
	//                SourceID points to existing image
	// - create bootable volume from volume.
	//   Assumptions: SourceType is "volume"
	//                Bootable is true
	//                SourceID points to existing volume
	// - create attachable empty volume.
	//   Assumptions: SourceType is "empty"
	//                Bootable is ignored
	//                SourceID is ignored
	// - create attachable volume from image?
	//   Assumptions: SourceType is "image"
	//                Bootable is false
	//                SourceID points to existing image
	// - create attachable volume from volume.
	//   Assumptions: SourceType is "volume"
	//                Bootable is false
	//                SourceID points to existing volume.
	// assume always persistent for now.
	// assume we have already checked quotas.
	// ID of source is the image id.
	switch s.SourceType {
	case types.ImageService:
		path, err := c.image.GetImagePath(s.SourceID)
		if err != nil {
			// this image doesn't exist
			return payloads.StorageResources{}, err
		}

		device, err := c.CreateBlockDevice(&path, s.Size)
		if err != nil {
			return payloads.StorageResources{}, err
		}

		// don't you need to add support for indicating whether
		// a block device is bootable.
		data := types.BlockData{
			BlockDevice: device,
			Size:        s.Size,
			CreateTime:  time.Now(),
			TenantID:    tenant,
			Name:        fmt.Sprintf("Storage for instance: %s", instanceID),
		}

		err = c.ds.AddBlockDevice(data)
		if err != nil {
			c.DeleteBlockDevice(device.ID)
			return payloads.StorageResources{}, err
		}

		return payloads.StorageResources{ID: data.ID, Bootable: s.Bootable, Ephemeral: true}, nil
	case types.VolumeService:
		device, err := c.CopyBlockDevice(s.SourceID)
		if err != nil {
			return payloads.StorageResources{}, err
		}

		// don't you need to add support for indicating whether
		// a block device is bootable.
		data := types.BlockData{
			BlockDevice: device,
			Size:        s.Size,
			CreateTime:  time.Now(),
			TenantID:    tenant,
			Name:        fmt.Sprintf("Storage for instance: %s", instanceID),
		}

		err = c.ds.AddBlockDevice(data)
		if err != nil {
			c.DeleteBlockDevice(device.ID)
			return payloads.StorageResources{}, err
		}

		return payloads.StorageResources{ID: data.ID, Bootable: s.Bootable}, nil

	case types.Empty:
		device, err := c.CreateBlockDevice(nil, s.Size)
		if err != nil {
			return payloads.StorageResources{}, err
		}

		// don't you need to add support for indicating whether
		// a block device is bootable.
		data := types.BlockData{
			BlockDevice: device,
			Size:        s.Size,
			CreateTime:  time.Now(),
			TenantID:    tenant,
			Name:        fmt.Sprintf("Storage for instance: %s", instanceID),
		}

		err = c.ds.AddBlockDevice(data)
		if err != nil {
			c.DeleteBlockDevice(device.ID)
			return payloads.StorageResources{}, err
		}

		return payloads.StorageResources{ID: data.ID, Bootable: false}, nil

	}

	return payloads.StorageResources{}, errors.New("Not implemented yet")
}

func newConfig(ctl *controller, wl *types.Workload, instanceID string, tenantID string) (config, error) {
	type UserData struct {
		UUID     string `json:"uuid"`
		Hostname string `json:"hostname"`
	}

	var userData UserData
	var config config

	baseConfig := wl.Config
	defaults := wl.Defaults
	imageID := wl.ImageID
	fwType := wl.FWType

	tenant, err := ctl.ds.GetTenant(tenantID)
	if err != nil {
		fmt.Println("unable to get tenant")
	}

	config.cnci = isCNCIWorkload(wl)

	var networking payloads.NetworkResources
	var storage payloads.StorageResources

	// do we ever need to save the vnic uuid?
	networking.VnicUUID = uuid.Generate().String()

	if config.cnci == false {
		ipAddress, err := ctl.ds.AllocateTenantIP(tenantID)
		if err != nil {
			fmt.Println("Unable to allocate IP address: ", err)
			return config, err
		}

		networking.VnicMAC = newTenantHardwareAddr(ipAddress).String()

		// send in CIDR notation?
		networking.PrivateIP = ipAddress.String()
		config.ip = ipAddress.String()
		mask := net.IPv4Mask(255, 255, 255, 0)
		ipnet := net.IPNet{
			IP:   ipAddress.Mask(mask),
			Mask: mask,
		}
		networking.Subnet = ipnet.String()
		networking.ConcentratorUUID = tenant.CNCIID

		// in theory we should refuse to go on if ip is null
		// for now let's keep going
		networking.ConcentratorIP = tenant.CNCIIP

		// set the hostname and uuid for userdata
		userData.UUID = instanceID
		userData.Hostname = instanceID
	} else {
		networking.VnicMAC = tenant.CNCIMAC

		// set the hostname and uuid for userdata
		userData.UUID = instanceID
		userData.Hostname = "cnci-" + tenantID
	}
	// handle storage resources
	if wl.Storage != nil {
		storage, err = getStorage(ctl, wl, tenantID, instanceID)
		if err != nil {
			return config, err
		}
	}

	// hardcode persistence until changes can be made to workload
	// template datastore.  Estimated resources can be blank
	// for now because we don't support it yet.
	startCmd := payloads.StartCmd{
		TenantUUID:          tenantID,
		InstanceUUID:        instanceID,
		ImageUUID:           imageID,
		FWType:              payloads.Firmware(fwType),
		VMType:              wl.VMType,
		InstancePersistence: payloads.Host,
		RequestedResources:  defaults,
		Networking:          networking,
		Storage:             storage,
	}

	if wl.VMType == payloads.Docker {
		startCmd.DockerImage = wl.ImageName
	}

	cmd := payloads.Start{
		Start: startCmd,
	}
	config.sc = cmd

	y, err := yaml.Marshal(&config.sc)
	if err != nil {
		glog.Warning("error marshalling config: ", err)
	}

	b, err := json.MarshalIndent(userData, "", "\t")
	if err != nil {
		glog.Warning("error marshalling user data: ", err)
	}

	config.config = "---\n" + string(y) + "...\n" + baseConfig + "---\n" + string(b) + "\n...\n"
	config.mac = networking.VnicMAC

	return config, err
}

func newTenantHardwareAddr(ip net.IP) net.HardwareAddr {
	buf := make([]byte, 6)
	ipBytes := ip.To4()
	buf[0] |= 2
	buf[1] = 0
	copy(buf[2:6], ipBytes)
	return net.HardwareAddr(buf)
}
<|endoftext|>