<commit_before>package plaid

import (
	"encoding/json"
	"errors"
	"time"
)

type Institution struct {
	Credentials  []Credential `json:"credentials"`
	HasMFA       bool         `json:"has_mfa"`
	ID           string       `json:"institution_id"`
	MFA          []string     `json:"mfa"`
	Name         string       `json:"name"`
	Products     []string     `json:"products"`
	CountryCodes []string     `json:"country_codes"`

	// Included when `options.include_status` is true.
	InstitutionStatus *InstitutionStatus `json:"status,omitempty"`

	// Included when `options.include_optional_metadata` is true.
	PrimaryColor string `json:"primary_color,omitempty"`
	// Included when `options.include_optional_metadata` is true.
	URL string `json:"url,omitempty"`
	// Included when `options.include_optional_metadata` is true.
	Logo string `json:"logo,omitempty"`
}

type InstitutionStatus struct {
	ItemLogins ItemLogins `json:"item_logins"`
}

type ItemLogins struct {
	Status           string                     `json:"status"`
	LastStatusChange time.Time                  `json:"last_status_change"`
	Breakdown        InstitutionStatusBreakdown `json:"breakdown"`
}

type InstitutionStatusBreakdown struct {
	Success          float64 `json:"success"`
	ErrorPlaid       float64 `json:"error_plaid"`
	ErrorInstitution float64 `json:"error_institution"`
}

type Credential struct {
	Label string `json:"label"`
	Name  string `json:"name"`
	Type  string `json:"type"`
}

type getInstitutionsRequest struct {
	ClientID string                 `json:"client_id"`
	Secret   string                 `json:"secret"`
	Count    int                    `json:"count"`
	Offset   int                    `json:"offset"`
	Options  GetInstitutionsOptions `json:"options,omitempty"`
}

type GetInstitutionsOptions struct {
	Products                []string `json:"products"`
	IncludeOptionalMetadata bool     `json:"include_optional_metadata"`
	CountryCodes            []string `json:"country_codes"`
}

type GetInstitutionsResponse struct {
	APIResponse
	Institutions []Institution `json:"institutions"`
	Total        int           `json:"total"`
}

type getInstitutionByIDRequest struct {
	ID        string                    `json:"institution_id"`
	PublicKey string                    `json:"public_key"`
	Options   GetInstitutionByIDOptions `json:"options,omitempty"`
}

type GetInstitutionByIDOptions struct {
	IncludeOptionalMetadata bool `json:"include_optional_metadata"`
	IncludeStatus           bool `json:"include_status"`
}

type GetInstitutionByIDResponse struct {
	APIResponse
	Institution Institution `json:"institution"`
}

type searchInstitutionsRequest struct {
	Query     string                    `json:"query"`
	Products  []string                  `json:"products"`
	PublicKey string                    `json:"public_key"`
	Options   SearchInstitutionsOptions `json:"options,omitempty"`
}

type SearchInstitutionsOptions struct {
	IncludeOptionalMetadata bool     `json:"include_optional_metadata"`
	CountryCodes            []string `json:"country_codes"`
}

type SearchInstitutionsResponse struct {
	APIResponse
	Institutions []Institution `json:"institutions"`
}

// GetInstitutionByID returns information for a single institution given an ID.
// See https://plaid.com/docs/api/#institutions-by-id.
func (c *Client) GetInstitutionByID(
	id string,
) (resp GetInstitutionByIDResponse, err error) {
	return c.GetInstitutionByIDWithOptions(id, GetInstitutionByIDOptions{})
}

// GetInstitutionByIDWithOptions returns information for a single institution given an ID.
// See https://plaid.com/docs/api/#institutions-by-id.
func (c *Client) GetInstitutionByIDWithOptions(
	id string,
	options GetInstitutionByIDOptions,
) (resp GetInstitutionByIDResponse, err error) {
	if id == "" {
		return resp, errors.New("/institutions/get_by_id - institution id must be specified")
	}

	jsonBody, err := json.Marshal(getInstitutionByIDRequest{
		ID:        id,
		PublicKey: c.publicKey,
		Options:   options,
	})

	if err != nil {
		return resp, err
	}

	err = c.Call("/institutions/get_by_id", jsonBody, &resp)
	return resp, err
}

// GetInstitutions returns information for all institutions supported by Plaid.
// See https://plaid.com/docs/api/#all-institutions.
func (c *Client) GetInstitutions(count, offset int) (resp GetInstitutionsResponse, err error) {
	return c.GetInstitutionsWithOptions(count, offset, GetInstitutionsOptions{})
}

// GetInstitutionsWithOptions returns information for all institutions supported by Plaid.
// See https://plaid.com/docs/api/#all-institutions.
func (c *Client) GetInstitutionsWithOptions(
	count int,
	offset int,
	options GetInstitutionsOptions,
) (resp GetInstitutionsResponse, err error) {
	if count == 0 {
		count = 50
	}

	jsonBody, err := json.Marshal(getInstitutionsRequest{
		ClientID: c.clientID,
		Secret:   c.secret,
		Count:    count,
		Offset:   offset,
		Options:  options,
	})

	if err != nil {
		return resp, err
	}

	err = c.Call("/institutions/get", jsonBody, &resp)
	return resp, err
}

// SearchInstitutions returns institutions corresponding to a query string and
// supported products.
// See https://plaid.com/docs/api/#institution-search.
func (c *Client) SearchInstitutions(
	query string,
	products []string,
) (resp SearchInstitutionsResponse, err error) {
	return c.SearchInstitutionsWithOptions(query, products, SearchInstitutionsOptions{})
}

// SearchInstitutionsWithOptions returns institutions corresponding to a query string and
// supported products.
// See https://plaid.com/docs/api/#institution-search.
func (c *Client) SearchInstitutionsWithOptions(
	query string,
	products []string,
	options SearchInstitutionsOptions,
) (resp SearchInstitutionsResponse, err error) {
	if query == "" {
		return resp, errors.New("/institutions/search - query must be specified")
	}

	jsonBody, err := json.Marshal(searchInstitutionsRequest{
		Query:     query,
		Products:  products,
		PublicKey: c.publicKey,
		Options:   options,
	})

	if err != nil {
		return resp, err
	}

	err = c.Call("/institutions/search", jsonBody, &resp)
	return resp, err
}
<commit_msg>Adding field options.account_filter to /institutions/search (#79)<commit_after>package plaid

import (
	"encoding/json"
	"errors"
	"time"
)

type Institution struct {
	Credentials  []Credential `json:"credentials"`
	HasMFA       bool         `json:"has_mfa"`
	ID           string       `json:"institution_id"`
	MFA          []string     `json:"mfa"`
	Name         string       `json:"name"`
	Products     []string     `json:"products"`
	CountryCodes []string     `json:"country_codes"`

	// Included when `options.include_status` is true.
	InstitutionStatus *InstitutionStatus `json:"status,omitempty"`

	// Included when `options.include_optional_metadata` is true.
	PrimaryColor string `json:"primary_color,omitempty"`
	// Included when `options.include_optional_metadata` is true.
	URL string `json:"url,omitempty"`
	// Included when `options.include_optional_metadata` is true.
	Logo string `json:"logo,omitempty"`
}

type InstitutionStatus struct {
	ItemLogins ItemLogins `json:"item_logins"`
}

type ItemLogins struct {
	Status           string                     `json:"status"`
	LastStatusChange time.Time                  `json:"last_status_change"`
	Breakdown        InstitutionStatusBreakdown `json:"breakdown"`
}

type InstitutionStatusBreakdown struct {
	Success          float64 `json:"success"`
	ErrorPlaid       float64 `json:"error_plaid"`
	ErrorInstitution float64 `json:"error_institution"`
}

type Credential struct {
	Label string `json:"label"`
	Name  string `json:"name"`
	Type  string `json:"type"`
}

type getInstitutionsRequest struct {
	ClientID string                 `json:"client_id"`
	Secret   string                 `json:"secret"`
	Count    int                    `json:"count"`
	Offset   int                    `json:"offset"`
	Options  GetInstitutionsOptions `json:"options,omitempty"`
}

type GetInstitutionsOptions struct {
	Products                []string `json:"products"`
	IncludeOptionalMetadata bool     `json:"include_optional_metadata"`
	CountryCodes            []string `json:"country_codes"`
}

type GetInstitutionsResponse struct {
	APIResponse
	Institutions []Institution `json:"institutions"`
	Total        int           `json:"total"`
}

type getInstitutionByIDRequest struct {
	ID        string                    `json:"institution_id"`
	PublicKey string                    `json:"public_key"`
	Options   GetInstitutionByIDOptions `json:"options,omitempty"`
}

type GetInstitutionByIDOptions struct {
	IncludeOptionalMetadata bool `json:"include_optional_metadata"`
	IncludeStatus           bool `json:"include_status"`
}

type GetInstitutionByIDResponse struct {
	APIResponse
	Institution Institution `json:"institution"`
}

type searchInstitutionsRequest struct {
	Query     string                    `json:"query"`
	Products  []string                  `json:"products"`
	PublicKey string                    `json:"public_key"`
	Options   SearchInstitutionsOptions `json:"options,omitempty"`
}

type SearchInstitutionsOptions struct {
	IncludeOptionalMetadata bool                   `json:"include_optional_metadata"`
	CountryCodes            []string               `json:"country_codes"`
	AccountFilter           map[string]interface{} `json:"account_filter"`
}

type SearchInstitutionsResponse struct {
	APIResponse
	Institutions []Institution `json:"institutions"`
}

// GetInstitutionByID returns information for a single institution given an ID.
// See https://plaid.com/docs/api/#institutions-by-id.
func (c *Client) GetInstitutionByID(
	id string,
) (resp GetInstitutionByIDResponse, err error) {
	return c.GetInstitutionByIDWithOptions(id, GetInstitutionByIDOptions{})
}

// GetInstitutionByIDWithOptions returns information for a single institution given an ID.
// See https://plaid.com/docs/api/#institutions-by-id.
func (c *Client) GetInstitutionByIDWithOptions(
	id string,
	options GetInstitutionByIDOptions,
) (resp GetInstitutionByIDResponse, err error) {
	if id == "" {
		return resp, errors.New("/institutions/get_by_id - institution id must be specified")
	}

	jsonBody, err := json.Marshal(getInstitutionByIDRequest{
		ID:        id,
		PublicKey: c.publicKey,
		Options:   options,
	})

	if err != nil {
		return resp, err
	}

	err = c.Call("/institutions/get_by_id", jsonBody, &resp)
	return resp, err
}

// GetInstitutions returns information for all institutions supported by Plaid.
// See https://plaid.com/docs/api/#all-institutions.
func (c *Client) GetInstitutions(count, offset int) (resp GetInstitutionsResponse, err error) {
	return c.GetInstitutionsWithOptions(count, offset, GetInstitutionsOptions{})
}

// GetInstitutionsWithOptions returns information for all institutions supported by Plaid.
// See https://plaid.com/docs/api/#all-institutions.
func (c *Client) GetInstitutionsWithOptions(
	count int,
	offset int,
	options GetInstitutionsOptions,
) (resp GetInstitutionsResponse, err error) {
	if count == 0 {
		count = 50
	}

	jsonBody, err := json.Marshal(getInstitutionsRequest{
		ClientID: c.clientID,
		Secret:   c.secret,
		Count:    count,
		Offset:   offset,
		Options:  options,
	})

	if err != nil {
		return resp, err
	}

	err = c.Call("/institutions/get", jsonBody, &resp)
	return resp, err
}

// SearchInstitutions returns institutions corresponding to a query string and
// supported products.
// See https://plaid.com/docs/api/#institution-search.
func (c *Client) SearchInstitutions(
	query string,
	products []string,
) (resp SearchInstitutionsResponse, err error) {
	return c.SearchInstitutionsWithOptions(query, products, SearchInstitutionsOptions{})
}

// SearchInstitutionsWithOptions returns institutions corresponding to a query string and
// supported products.
// See https://plaid.com/docs/api/#institution-search.
func (c *Client) SearchInstitutionsWithOptions(
	query string,
	products []string,
	options SearchInstitutionsOptions,
) (resp SearchInstitutionsResponse, err error) {
	if query == "" {
		return resp, errors.New("/institutions/search - query must be specified")
	}

	jsonBody, err := json.Marshal(searchInstitutionsRequest{
		Query:     query,
		Products:  products,
		PublicKey: c.publicKey,
		Options:   options,
	})

	if err != nil {
		return resp, err
	}

	err = c.Call("/institutions/search", jsonBody, &resp)
	return resp, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/pachyderm-io/pfs/lib/btrfs"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
)

//TODO commits should be content based

func commitParam(r *http.Request) string {
	if c := r.URL.Query().Get("commit"); c != "" {
		return c
	}
	return "master"
}

func branchParam(r *http.Request) string {
	if c := r.URL.Query().Get("branch"); c != "" {
		return c
	}
	return "master"
}

// PfsHandler is the core route for modifying the contents of the fileystem.
// Changes are not replicated until a call to CommitHandler.
func PfsHandler(w http.ResponseWriter, r *http.Request, fs *btrfs.FS) {
	url := strings.Split(r.URL.Path, "/")
	// commitFile is used for read methods (GET)
	commitFile := path.Join(append([]string{path.Join("repo", commitParam(r))}, url[2:]...)...)
	// branchFile is used for write methods (POST, PUT, DELETE)
	branchFile := path.Join(append([]string{path.Join("repo", branchParam(r))}, url[2:]...)...)

	if r.Method == "GET" {
		if f, err := fs.Open(commitFile); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		} else {
			io.Copy(w, f)
		}
	} else if r.Method == "POST" {
		size, err := fs.CreateFromReader(branchFile, r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created %s, size: %d.\n", branchFile, size)
	} else if r.Method == "PUT" {
		size, err := fs.WriteFile(branchFile, r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created %s, size: %d.\n", branchFile, size)
	} else if r.Method == "DELETE" {
		if err := fs.Remove(branchFile); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Deleted %s.\n", branchFile)
	}
}

// CommitHandler creates a snapshot of outstanding changes.
func CommitHandler(w http.ResponseWriter, r *http.Request, fs *btrfs.FS) {
	var commit string
	var err error
	if commit, err = fs.Commit("repo", commitParam(r), branchParam(r)); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}

	fmt.Fprintf(w, "Create commit: %s.\n", commit)
}

// BranchHandler creates a new branch from commit.
func BranchHandler(w http.ResponseWriter, r *http.Request, fs *btrfs.FS) {
	if r.Method != "POST" {
		http.Error(w, "Invalid method.", 405)
		log.Print("Invalid method %s.", r.Method)
		return
	}

	if err := fs.Branch("repo", commitParam(r), branchParam(r)); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
	fmt.Fprintf(w, "Created branch. (%s) -> %s.\n", commitParam(r), branchParam(r))
}

// MasterMux creates a multiplexer for a Master writing to the passed in FS.
func MasterMux(fs *btrfs.FS) *http.ServeMux {
	mux := http.NewServeMux()

	commitHandler := func(w http.ResponseWriter, r *http.Request) {
		CommitHandler(w, r, fs)
	}

	pfsHandler := func(w http.ResponseWriter, r *http.Request) {
		PfsHandler(w, r, fs)
	}

	branchHandler := func(w http.ResponseWriter, r *http.Request) {
		BranchHandler(w, r, fs)
	}

	mux.HandleFunc("/commit", commitHandler)
	mux.HandleFunc("/pfs/", pfsHandler)
	mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "pong\n") })
	mux.HandleFunc("/branch", branchHandler)

	return mux
}

// RunServer runs a master server listening on port 80
func RunServer(fs *btrfs.FS) {
	http.ListenAndServe(":80", MasterMux(fs))
}

func main() {
	log.SetFlags(log.Lshortfile)
	fs := btrfs.NewFSWithRandSeq("master-" + os.Args[1])
	if err := fs.EnsureNamespace(); err != nil {
		log.Fatal(err)
	}
	if err := fs.Init("repo"); err != nil {
		log.Fatal(err)
	}
	log.Print("Listening on port 80...")
	RunServer(fs)
}
<commit_msg>First stab at supporting wildcards in the master.<commit_after>package main

import (
	"fmt"
	"github.com/pachyderm-io/pfs/lib/btrfs"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
)

//TODO commits should be content based

func commitParam(r *http.Request) string {
	if c := r.URL.Query().Get("commit"); c != "" {
		return c
	}
	return "master"
}

func branchParam(r *http.Request) string {
	if c := r.URL.Query().Get("branch"); c != "" {
		return c
	}
	return "master"
}

func cat(w http.ResponseWriter, name string, fs *btrfs.FS) {
	f, err := fs.Open(name)
	defer f.Close()
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
	}

	if _, err := io.Copy(w, f); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
	}
}

// PfsHandler is the core route for modifying the contents of the fileystem.
// Changes are not replicated until a call to CommitHandler.
func PfsHandler(w http.ResponseWriter, r *http.Request, fs *btrfs.FS) {
	url := strings.Split(r.URL.Path, "/")
	// commitFile is used for read methods (GET)
	commitFile := path.Join(append([]string{path.Join("repo", commitParam(r))}, url[2:]...)...)
	// branchFile is used for write methods (POST, PUT, DELETE)
	branchFile := path.Join(append([]string{path.Join("repo", branchParam(r))}, url[2:]...)...)

	if r.Method == "GET" {
		if strings.Contains(commitFile, "*") {
			if !strings.HasSuffix(commitFile, "*") {
				http.Error(w, "Illegal path containing internal `*`. `*` is currently only allowed as the last character of a path.", 400)
			} else {
				files, err := fs.ReadDir(path.Dir(commitFile))
				if err != nil {
					http.Error(w, err.Error(), 500)
					return
				} else {
					for _, fi := range files {
						if fi.IsDir() {
							continue
						} else {
							cat(w, fi.Name(), fs)
						}
					}
				}
			}
		} else {
			cat(w, commitFile, fs)
		}
	} else if r.Method == "POST" {
		size, err := fs.CreateFromReader(branchFile, r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created %s, size: %d.\n", branchFile, size)
	} else if r.Method == "PUT" {
		size, err := fs.WriteFile(branchFile, r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created %s, size: %d.\n", branchFile, size)
	} else if r.Method == "DELETE" {
		if err := fs.Remove(branchFile); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Deleted %s.\n", branchFile)
	}
}

// CommitHandler creates a snapshot of outstanding changes.
func CommitHandler(w http.ResponseWriter, r *http.Request, fs *btrfs.FS) {
	var commit string
	var err error
	if commit, err = fs.Commit("repo", commitParam(r), branchParam(r)); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}

	fmt.Fprintf(w, "Create commit: %s.\n", commit)
}

// BranchHandler creates a new branch from commit.
func BranchHandler(w http.ResponseWriter, r *http.Request, fs *btrfs.FS) {
	if r.Method != "POST" {
		http.Error(w, "Invalid method.", 405)
		log.Print("Invalid method %s.", r.Method)
		return
	}

	if err := fs.Branch("repo", commitParam(r), branchParam(r)); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
	fmt.Fprintf(w, "Created branch. (%s) -> %s.\n", commitParam(r), branchParam(r))
}

// MasterMux creates a multiplexer for a Master writing to the passed in FS.
func MasterMux(fs *btrfs.FS) *http.ServeMux {
	mux := http.NewServeMux()

	commitHandler := func(w http.ResponseWriter, r *http.Request) {
		CommitHandler(w, r, fs)
	}

	pfsHandler := func(w http.ResponseWriter, r *http.Request) {
		PfsHandler(w, r, fs)
	}

	branchHandler := func(w http.ResponseWriter, r *http.Request) {
		BranchHandler(w, r, fs)
	}

	mux.HandleFunc("/commit", commitHandler)
	mux.HandleFunc("/pfs/", pfsHandler)
	mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "pong\n") })
	mux.HandleFunc("/branch", branchHandler)

	return mux
}

// RunServer runs a master server listening on port 80
func RunServer(fs *btrfs.FS) {
	http.ListenAndServe(":80", MasterMux(fs))
}

func main() {
	log.SetFlags(log.Lshortfile)
	fs := btrfs.NewFSWithRandSeq("master-" + os.Args[1])
	if err := fs.EnsureNamespace(); err != nil {
		log.Fatal(err)
	}
	if err := fs.Init("repo"); err != nil {
		log.Fatal(err)
	}
	log.Print("Listening on port 80...")
	RunServer(fs)
}
<|endoftext|>


<commit_before>package export

import (
	cc "github.com/pivotalservices/cf-mgmt/cloudcontroller"
	"github.com/pivotalservices/cf-mgmt/config"
	"github.com/pivotalservices/cf-mgmt/organization"
	"github.com/pivotalservices/cf-mgmt/space"
	"github.com/pivotalservices/cf-mgmt/uaac"
	"github.com/xchapter7x/lo"
)

//NewExportManager Creates a new instance of the ImportConfig manager
func NewExportManager(
	configDir string,
	uaacMgr uaac.Manager,
	cloudController cc.Manager) Manager {
	return &DefaultImportManager{
		ConfigDir:       configDir,
		UAACMgr:         uaacMgr,
		CloudController: cloudController,
	}
}

//ExportConfig Imports org and space configuration from an existing CF instance
//Entries part of excludedOrgs and excludedSpaces are not included in the import
func (im *DefaultImportManager) ExportConfig(excludedOrgs map[string]string, excludedSpaces map[string]string) error {
	var err error
	var orgs []*cc.Org
	var configMgr config.Manager
	var userIDToUserMap map[string]uaac.User
	var spaces []*cc.Space

	//Get all the users from the foundation
	userIDToUserMap, err = im.UAACMgr.UsersByID()
	if err != nil {
		lo.G.Error("Unable to retrieve users")
		return err
	}
	//Get all the orgs
	orgs, err = im.CloudController.ListOrgs()
	if err != nil {
		lo.G.Errorf("Unable to retrieve orgs. Error : %s", err)
		return err
	}
	configMgr = config.NewManager(im.ConfigDir)
	lo.G.Info("Trying to delete existing config directory")
	//Delete existing config directory
	configMgr.DeleteConfigIfExists()
	if err != nil {
		return err
	}
	//Create a brand new directory
	lo.G.Info("Trying to create new config folder")

	var uaaUserOrigin string
	for _, usr := range userIDToUserMap {
		if usr.Origin != "" {
			uaaUserOrigin = usr.Origin
			break
		}
	}
	lo.G.Infof("Using UAA user origin: %s", uaaUserOrigin)
	err = configMgr.CreateConfigIfNotExists(uaaUserOrigin)
	if err != nil {
		return err
	}
	lo.G.Debugf("Orgs to process: %s", orgs)

	for _, org := range orgs {
		if _, ok := excludedOrgs[org.Entity.Name]; !ok {
			lo.G.Infof("Processing org: %s ", org.Entity.Name)
			orgConfig := &config.OrgConfig{OrgName: org.Entity.Name}
			//Add users
			addOrgUsers(orgConfig, im.CloudController, userIDToUserMap, org.MetaData.GUID)
			//Add Quota definition if applicable
			if org.Entity.QuotaDefinitionGUID != "" {
				orgConfig.OrgQuota = quotaDefinition(im.CloudController, org.Entity.QuotaDefinitionGUID, organization.ORGS)
			}
			configMgr.AddOrgToConfig(orgConfig)

			lo.G.Infof("Done creating org %s", orgConfig.OrgName)
			lo.G.Infof("Listing spaces for org %s", orgConfig.OrgName)
			spaces, _ = im.CloudController.ListSpaces(org.MetaData.GUID)
			lo.G.Infof("Found %d Spaces for org %s", len(spaces), orgConfig.OrgName)
			for _, orgSpace := range spaces {
				if _, ok := excludedSpaces[orgSpace.Entity.Name]; !ok {
					lo.G.Infof("Processing space: %s", orgSpace.Entity.Name)

					spaceConfig := &config.SpaceConfig{OrgName: org.Entity.Name, SpaceName: orgSpace.Entity.Name}
					//Add users
					addSpaceUsers(spaceConfig, im.CloudController, userIDToUserMap, orgSpace.MetaData.GUID)
					//Add Quota definition if applicable
					if orgSpace.Entity.QuotaDefinitionGUID != "" {
						spaceConfig.SpaceQuota = quotaDefinition(im.CloudController, orgSpace.Entity.QuotaDefinitionGUID, space.SPACES)
					}
					if orgSpace.Entity.AllowSSH {
						spaceConfig.AllowSSH = true
					}
					configMgr.AddSpaceToConfig(spaceConfig)
				} else {
					lo.G.Infof("Skipping space: %s as it is ignored from import", orgSpace.Entity.Name)
				}
			}
		} else {
			lo.G.Infof("Skipping org: %s as it is ignored from import", org.Entity.Name)
		}
	}
	return nil
}

func quotaDefinition(controller cc.Manager, quotaDefinitionGUID, entityType string) cc.QuotaEntity {
	quotaDef, _ := controller.QuotaDef(quotaDefinitionGUID, entityType)
	if quotaDef.Entity.Name != "default" {
		return quotaDef.Entity
	}
	return cc.QuotaEntity{}
}

func addOrgUsers(orgConfig *config.OrgConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, orgGUID string) {
	addOrgManagers(orgConfig, controller, userIDToUserMap, orgGUID)
	addBillingManagers(orgConfig, controller, userIDToUserMap, orgGUID)
	addOrgAuditors(orgConfig, controller, userIDToUserMap, orgGUID)
}

func addSpaceUsers(spaceConfig *config.SpaceConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, spaceGUID string) {
	addSpaceDevelopers(spaceConfig, controller, userIDToUserMap, spaceGUID)
	addSpaceManagers(spaceConfig, controller, userIDToUserMap, spaceGUID)
	addSpaceAuditors(spaceConfig, controller, userIDToUserMap, spaceGUID)
}

func addOrgManagers(orgConfig *config.OrgConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, orgGUID string) {
	orgMgrs, _ := getCFUsers(controller, orgGUID, organization.ORGS, organization.ROLE_ORG_MANAGERS)
	lo.G.Debugf("Found %d Org Managers for Org: %s", len(orgMgrs), orgConfig.OrgName)
	doAddUsers(orgMgrs, &orgConfig.OrgMgrUAAUsers, &orgConfig.OrgMgrLDAPUsers, userIDToUserMap)
}

func addBillingManagers(orgConfig *config.OrgConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, orgGUID string) {
	orgBillingMgrs, _ := getCFUsers(controller, orgGUID, organization.ORGS, organization.ROLE_ORG_BILLING_MANAGERS)
	lo.G.Debugf("Found %d Org Billing Managers for Org: %s", len(orgBillingMgrs), orgConfig.OrgName)
	doAddUsers(orgBillingMgrs, &orgConfig.OrgBillingMgrUAAUsers, &orgConfig.OrgBillingMgrLDAPUsers, userIDToUserMap)
}

func addOrgAuditors(orgConfig *config.OrgConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, orgGUID string) {
	orgAuditors, _ := getCFUsers(controller, orgGUID, organization.ORGS, organization.ROLE_ORG_AUDITORS)
	lo.G.Debugf("Found %d Org Auditors for Org: %s", len(orgAuditors), orgConfig.OrgName)
	doAddUsers(orgAuditors, &orgConfig.OrgAuditorUAAUsers, &orgConfig.OrgAuditorLDAPUsers, userIDToUserMap)
}

func addSpaceManagers(spaceConfig *config.SpaceConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, spaceGUID string) {
	spaceMgrs, _ := getCFUsers(controller, spaceGUID, space.SPACES, space.ROLE_SPACE_MANAGERS)
	lo.G.Debugf("Found %d Space Managers for Org: %s and  Space:  %s", len(spaceMgrs), spaceConfig.OrgName, spaceConfig.SpaceName)
	doAddUsers(spaceMgrs, &spaceConfig.SpaceMgrUAAUsers, &spaceConfig.SpaceMgrLDAPUsers, userIDToUserMap)
}

func addSpaceDevelopers(spaceConfig *config.SpaceConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, spaceGUID string) {
	spaceDevs, _ := getCFUsers(controller, spaceGUID, space.SPACES, space.ROLE_SPACE_DEVELOPERS)
	lo.G.Debugf("Found %d Space Developers for Org: %s and  Space:  %s", len(spaceDevs), spaceConfig.OrgName, spaceConfig.SpaceName)
	doAddUsers(spaceDevs, &spaceConfig.SpaceDevUAAUsers, &spaceConfig.SpaceDevLDAPUsers, userIDToUserMap)
}

func addSpaceAuditors(spaceConfig *config.SpaceConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, spaceGUID string) {
	spaceAuditors, _ := getCFUsers(controller, spaceGUID, space.SPACES, space.ROLE_SPACE_AUDITORS)
	lo.G.Debugf("Found %d Space Auditors for Org: %s and  Space:  %s", len(spaceAuditors), spaceConfig.OrgName, spaceConfig.SpaceName)
	doAddUsers(spaceAuditors, &spaceConfig.SpaceAuditorUAAUsers, &spaceConfig.SpaceAuditorLDAPUsers, userIDToUserMap)
}

func doAddUsers(cfUsers map[string]string, uaaUsers *[]string, ldapUsers *[]string, userIDToUserMap map[string]uaac.User) {
	for cfUser := range cfUsers {
		if usr, ok := userIDToUserMap[cfUser]; ok {
			if usr.Origin == "uaa" {
				*uaaUsers = append(*uaaUsers, usr.UserName)
			} else {
				*ldapUsers = append(*ldapUsers, usr.UserName)
			}
		}
	}
}

func getCFUsers(cc cc.Manager, entityGUID, entityType, role string) (map[string]string, error) {
	return cc.GetCFUsers(entityGUID, entityType, role)
}
<commit_msg>exportconfig cleanup<commit_after>package export

import (
	cc "github.com/pivotalservices/cf-mgmt/cloudcontroller"
	"github.com/pivotalservices/cf-mgmt/config"
	"github.com/pivotalservices/cf-mgmt/organization"
	"github.com/pivotalservices/cf-mgmt/space"
	"github.com/pivotalservices/cf-mgmt/uaac"
	"github.com/xchapter7x/lo"
)

//NewExportManager Creates a new instance of the ImportConfig manager
func NewExportManager(
	configDir string,
	uaacMgr uaac.Manager,
	cloudController cc.Manager) Manager {
	return &DefaultImportManager{
		ConfigDir:       configDir,
		UAACMgr:         uaacMgr,
		CloudController: cloudController,
	}
}

//ExportConfig Imports org and space configuration from an existing CF instance
//Entries part of excludedOrgs and excludedSpaces are not included in the import
func (im *DefaultImportManager) ExportConfig(excludedOrgs map[string]string, excludedSpaces map[string]string) error {
	//Get all the users from the foundation
	userIDToUserMap, err := im.UAACMgr.UsersByID()
	if err != nil {
		lo.G.Error("Unable to retrieve users")
		return err
	}
	//Get all the orgs
	orgs, err := im.CloudController.ListOrgs()
	if err != nil {
		lo.G.Errorf("Unable to retrieve orgs. Error : %s", err)
		return err
	}
	configMgr := config.NewManager(im.ConfigDir)
	lo.G.Info("Trying to delete existing config directory")
	//Delete existing config directory
	err = configMgr.DeleteConfigIfExists()
	if err != nil {
		return err
	}
	//Create a brand new directory
	lo.G.Info("Trying to create new config folder")

	var uaaUserOrigin string
	for _, usr := range userIDToUserMap {
		if usr.Origin != "" {
			uaaUserOrigin = usr.Origin
			break
		}
	}
	lo.G.Infof("Using UAA user origin: %s", uaaUserOrigin)
	err = configMgr.CreateConfigIfNotExists(uaaUserOrigin)
	if err != nil {
		return err
	}
	lo.G.Debugf("Orgs to process: %s", orgs)

	for _, org := range orgs {
		if _, ok := excludedOrgs[org.Entity.Name]; ok {
			lo.G.Infof("Skipping org: %s as it is ignored from import", org.Entity.Name)
			continue
		}
		lo.G.Infof("Processing org: %s ", org.Entity.Name)
		orgConfig := &config.OrgConfig{OrgName: org.Entity.Name}
		//Add users
		addOrgUsers(orgConfig, im.CloudController, userIDToUserMap, org.MetaData.GUID)
		//Add Quota definition if applicable
		if org.Entity.QuotaDefinitionGUID != "" {
			orgConfig.OrgQuota = quotaDefinition(im.CloudController, org.Entity.QuotaDefinitionGUID, organization.ORGS)
		}
		configMgr.AddOrgToConfig(orgConfig)

		lo.G.Infof("Done creating org %s", orgConfig.OrgName)
		lo.G.Infof("Listing spaces for org %s", orgConfig.OrgName)
		spaces, _ := im.CloudController.ListSpaces(org.MetaData.GUID)
		lo.G.Infof("Found %d Spaces for org %s", len(spaces), orgConfig.OrgName)
		for _, orgSpace := range spaces {
			if _, ok := excludedSpaces[orgSpace.Entity.Name]; ok {
				lo.G.Infof("Skipping space: %s as it is ignored from import", orgSpace.Entity.Name)
				continue
			}
			lo.G.Infof("Processing space: %s", orgSpace.Entity.Name)

			spaceConfig := &config.SpaceConfig{OrgName: org.Entity.Name, SpaceName: orgSpace.Entity.Name}
			//Add users
			addSpaceUsers(spaceConfig, im.CloudController, userIDToUserMap, orgSpace.MetaData.GUID)
			//Add Quota definition if applicable
			if orgSpace.Entity.QuotaDefinitionGUID != "" {
				spaceConfig.SpaceQuota = quotaDefinition(im.CloudController, orgSpace.Entity.QuotaDefinitionGUID, space.SPACES)
			}
			if orgSpace.Entity.AllowSSH {
				spaceConfig.AllowSSH = true
			}
			configMgr.AddSpaceToConfig(spaceConfig)
		}
	}
	return nil
}

func quotaDefinition(controller cc.Manager, quotaDefinitionGUID, entityType string) cc.QuotaEntity {
	quotaDef, _ := controller.QuotaDef(quotaDefinitionGUID, entityType)
	if quotaDef.Entity.Name != "default" {
		return quotaDef.Entity
	}
	return cc.QuotaEntity{}
}

func addOrgUsers(orgConfig *config.OrgConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, orgGUID string) {
	addOrgManagers(orgConfig, controller, userIDToUserMap, orgGUID)
	addBillingManagers(orgConfig, controller, userIDToUserMap, orgGUID)
	addOrgAuditors(orgConfig, controller, userIDToUserMap, orgGUID)
}

func addSpaceUsers(spaceConfig *config.SpaceConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, spaceGUID string) {
	addSpaceDevelopers(spaceConfig, controller, userIDToUserMap, spaceGUID)
	addSpaceManagers(spaceConfig, controller, userIDToUserMap, spaceGUID)
	addSpaceAuditors(spaceConfig, controller, userIDToUserMap, spaceGUID)
}

func addOrgManagers(orgConfig *config.OrgConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, orgGUID string) {
	orgMgrs, _ := getCFUsers(controller, orgGUID, organization.ORGS, organization.ROLE_ORG_MANAGERS)
	lo.G.Debugf("Found %d Org Managers for Org: %s", len(orgMgrs), orgConfig.OrgName)
	doAddUsers(orgMgrs, &orgConfig.OrgMgrUAAUsers, &orgConfig.OrgMgrLDAPUsers, userIDToUserMap)
}

func addBillingManagers(orgConfig *config.OrgConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, orgGUID string) {
	orgBillingMgrs, _ := getCFUsers(controller, orgGUID, organization.ORGS, organization.ROLE_ORG_BILLING_MANAGERS)
	lo.G.Debugf("Found %d Org Billing Managers for Org: %s", len(orgBillingMgrs), orgConfig.OrgName)
	doAddUsers(orgBillingMgrs, &orgConfig.OrgBillingMgrUAAUsers, &orgConfig.OrgBillingMgrLDAPUsers, userIDToUserMap)
}

func addOrgAuditors(orgConfig *config.OrgConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, orgGUID string) {
	orgAuditors, _ := getCFUsers(controller, orgGUID, organization.ORGS, organization.ROLE_ORG_AUDITORS)
	lo.G.Debugf("Found %d Org Auditors for Org: %s", len(orgAuditors), orgConfig.OrgName)
	doAddUsers(orgAuditors, &orgConfig.OrgAuditorUAAUsers, &orgConfig.OrgAuditorLDAPUsers, userIDToUserMap)
}

func addSpaceManagers(spaceConfig *config.SpaceConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, spaceGUID string) {
	spaceMgrs, _ := getCFUsers(controller, spaceGUID, space.SPACES, space.ROLE_SPACE_MANAGERS)
	lo.G.Debugf("Found %d Space Managers for Org: %s and  Space:  %s", len(spaceMgrs), spaceConfig.OrgName, spaceConfig.SpaceName)
	doAddUsers(spaceMgrs, &spaceConfig.SpaceMgrUAAUsers, &spaceConfig.SpaceMgrLDAPUsers, userIDToUserMap)
}

func addSpaceDevelopers(spaceConfig *config.SpaceConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, spaceGUID string) {
	spaceDevs, _ := getCFUsers(controller, spaceGUID, space.SPACES, space.ROLE_SPACE_DEVELOPERS)
	lo.G.Debugf("Found %d Space Developers for Org: %s and  Space:  %s", len(spaceDevs), spaceConfig.OrgName, spaceConfig.SpaceName)
	doAddUsers(spaceDevs, &spaceConfig.SpaceDevUAAUsers, &spaceConfig.SpaceDevLDAPUsers, userIDToUserMap)
}

func addSpaceAuditors(spaceConfig *config.SpaceConfig, controller cc.Manager, userIDToUserMap map[string]uaac.User, spaceGUID string) {
	spaceAuditors, _ := getCFUsers(controller, spaceGUID, space.SPACES, space.ROLE_SPACE_AUDITORS)
	lo.G.Debugf("Found %d Space Auditors for Org: %s and  Space:  %s", len(spaceAuditors), spaceConfig.OrgName, spaceConfig.SpaceName)
	doAddUsers(spaceAuditors, &spaceConfig.SpaceAuditorUAAUsers, &spaceConfig.SpaceAuditorLDAPUsers, userIDToUserMap)
}

func doAddUsers(cfUsers map[string]string, uaaUsers *[]string, ldapUsers *[]string, userIDToUserMap map[string]uaac.User) {
	for cfUser := range cfUsers {
		if usr, ok := userIDToUserMap[cfUser]; ok {
			if usr.Origin == "uaa" {
				*uaaUsers = append(*uaaUsers, usr.UserName)
			} else {
				*ldapUsers = append(*ldapUsers, usr.UserName)
			}
		}
	}
}

func getCFUsers(cc cc.Manager, entityGUID, entityType, role string) (map[string]string, error) {
	return cc.GetCFUsers(entityGUID, entityType, role)
}
<|endoftext|>


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

package client

import (
	"bytes"
	"encoding/json"
	"path"
	"sort"
	"strings"
	"sync"
)

// TestConnection is a test connection type
type TestConnection struct {
	id      int
	nodes   map[string][]byte
	watches map[string][]chan<- Event
	Err     error // Connection Error set by the user
	lock    sync.RWMutex
}

// NewTestConnection initializes a new test connection
func NewTestConnection() *TestConnection {
	return new(TestConnection).init()
}

func (conn *TestConnection) init() *TestConnection {
	conn = &TestConnection{
		nodes:   make(map[string][]byte),
		watches: make(map[string][]chan<- Event),
	}
	conn.lock = sync.RWMutex{}
	conn.nodes["/"] = nil
	return conn
}

func (conn *TestConnection) checkpath(p *string) error {
	*p = path.Clean(*p)
	return conn.Err
}

func (conn *TestConnection) updatewatch(p string, eventtype EventType) {
	conn.lock.Lock()
	defer conn.lock.Unlock()
	if watches := conn.watches[p]; watches != nil && len(watches) > 0 {
		delete(conn.watches, p)
		for _, watch := range watches {
			watch <- Event{eventtype, p, nil}
		}
	}

	parent := path.Dir(p)
	if watches := conn.watches[parent]; watches != nil && len(watches) > 0 {
		delete(conn.watches, parent)
		for _, watch := range watches {
			watch <- Event{EventNodeChildrenChanged, parent, nil}
		}
	}
}

func (conn *TestConnection) addwatch(p string) <-chan Event {
	eventC := make(chan Event, 1)
	conn.lock.Lock()
	watches := conn.watches[p]
	conn.watches[p] = append(watches, eventC)
	conn.lock.Unlock()
	return eventC
}

// Close implements Connection.Close
func (conn *TestConnection) Close() {
	var dirW, nodeW []string

	// Organize by child watch vs node watch
	for p := range conn.watches {
		if strings.HasSuffix(p, "/") {
			dirW = append(dirW, p)
		} else {
			nodeW = append(nodeW, p)
		}
	}

	// Signal the top-level path first and continue down the node tree
	sort.Strings(dirW)
	for _, d := range dirW {
		conn.updatewatch(d, EventSession)
	}

	// Signal all of the nodes
	for _, n := range nodeW {
		conn.updatewatch(n, EventSession)
	}
}

// SetID implements Connection.SetID
func (conn *TestConnection) SetID(id int) { conn.id = id }

// ID implements Connection.ID
func (conn *TestConnection) ID() int { return conn.id }

// SetOnClose implements Connection.SetOnClose
func (conn *TestConnection) SetOnClose(f func(int)) {}

// Create implements Connection.Create
func (conn *TestConnection) Create(p string, node Node) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	if node := conn.nodes[p]; node != nil {
		return ErrNodeExists
	} else if err := conn.CreateDir(p); err != nil {
		return err
	}

	data, err := json.Marshal(node)
	if err != nil {
		return err
	}
	conn.lock.Lock()
	conn.nodes[p] = data
	conn.lock.Unlock()
	return nil
}

// CreateDir implements Connection.CreateDir
func (conn *TestConnection) CreateDir(p string) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	if _, exists := conn.nodes[p]; exists {
		return nil
	} else if err := conn.CreateDir(path.Dir(p)); err != nil {
		return err
	}
	conn.lock.Lock()
	conn.nodes[p] = nil
	conn.lock.Unlock()
	conn.updatewatch(p, EventNodeCreated)
	return nil
}

// Exists implements Connection.Exists
func (conn *TestConnection) Exists(p string) (bool, error) {
	if err := conn.checkpath(&p); err != nil {
		return false, err
	}

	conn.lock.RLock()
	defer conn.lock.RUnlock()
	if _, exists := conn.nodes[p]; !exists {
		return false, ErrNoNode
	}

	return true, nil
}

// Delete implements Connection.Delete
func (conn *TestConnection) Delete(p string) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	children, _ := conn.Children(p)
	for _, c := range children {
		if err := conn.Delete(path.Join(p, c)); err != nil {
			return err
		}
	}
	if _, ok := conn.nodes[p]; ok {
		delete(conn.nodes, p)
	}
	conn.updatewatch(p, EventNodeDeleted)
	return nil
}

// ChildrenW implements Connection.ChildrenW
func (conn *TestConnection) ChildrenW(p string) ([]string, <-chan Event, error) {
	if err := conn.checkpath(&p); err != nil {
		return nil, nil, err
	}

	children, err := conn.Children(p)
	if err != nil {
		return nil, nil, err
	}

	return children, conn.addwatch(p), nil
}

// Children implements Connection.Children
func (conn *TestConnection) Children(p string) (children []string, err error) {
	if err := conn.checkpath(&p); err != nil {
		return nil, err
	}

	pattern := path.Join(p, "*")
	conn.lock.Lock()
	defer conn.lock.Unlock()
	for nodepath, _ := range conn.nodes {
		if match, _ := path.Match(pattern, nodepath); match {
			children = append(children, strings.TrimPrefix(nodepath, p+"/"))
		}
	}

	return children, nil
}

// GetW implements Connection.GetW
func (conn *TestConnection) GetW(p string, node Node) (<-chan Event, error) {
	if err := conn.checkpath(&p); err != nil {
		return nil, err
	}

	if err := conn.Get(p, node); err != nil {
		return nil, err
	}

	return conn.addwatch(p), nil
}

// Get implements Connection.Get
func (conn *TestConnection) Get(p string, node Node) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	conn.lock.RLock()
	defer conn.lock.RUnlock()
	if data, ok := conn.nodes[p]; !ok {
		return ErrNoNode
	} else if data == nil {
		return ErrEmptyNode
	} else if err := json.Unmarshal(data, node); err != nil {
		return err
	}
	return nil
}

// Set implements Connection.Set
func (conn *TestConnection) Set(p string, node Node) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	if _, ok := conn.nodes[p]; !ok {
		return ErrNoNode
	}

	data, err := json.Marshal(node)
	if err != nil {
		return err
	}

	// only update if something actually changed
	if bytes.Compare(conn.nodes[p], data) != 0 {
		conn.lock.Lock()
		conn.nodes[p] = data
		conn.lock.Unlock()
		conn.updatewatch(p, EventNodeDataChanged)
	}
	return nil
}

type TestLock struct {
}

func (l TestLock) Lock() error {
	return nil
}

func (l TestLock) Unlock() error {
	return nil
}

// NewLock implements Connection.NewLock
func (conn *TestConnection) NewLock(path string) Lock {
	return TestLock{}
}

// NewLeader implements Connection.NewLeader
func (conn *TestConnection) NewLeader(path string, data Node) Leader {
	return nil
}

// CreateEphemeral implements Connection.CreateEphemeral
func (conn *TestConnection) CreateEphemeral(path string, node Node) (string, error) {
	if err := conn.Create(path, node); err != nil {
		return "", err
	}
	return path, nil
}
<commit_msg>Fixed race condition<commit_after>// Copyright 2014 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package client

import (
	"bytes"
	"encoding/json"
	"path"
	"sort"
	"strings"
	"sync"
)

// TestConnection is a test connection type
type TestConnection struct {
	id      int
	nodes   map[string][]byte
	watches map[string][]chan<- Event
	Err     error // Connection Error set by the user
	lock    sync.RWMutex
}

// NewTestConnection initializes a new test connection
func NewTestConnection() *TestConnection {
	return new(TestConnection).init()
}

func (conn *TestConnection) init() *TestConnection {
	conn = &TestConnection{
		nodes:   make(map[string][]byte),
		watches: make(map[string][]chan<- Event),
	}
	conn.lock = sync.RWMutex{}
	conn.nodes["/"] = nil
	return conn
}

func (conn *TestConnection) checkpath(p *string) error {
	*p = path.Clean(*p)
	return conn.Err
}

func (conn *TestConnection) updatewatch(p string, eventtype EventType) {
	conn.lock.Lock()
	defer conn.lock.Unlock()
	if watches := conn.watches[p]; watches != nil && len(watches) > 0 {
		delete(conn.watches, p)
		for _, watch := range watches {
			watch <- Event{eventtype, p, nil}
		}
	}

	parent := path.Dir(p)
	if watches := conn.watches[parent]; watches != nil && len(watches) > 0 {
		delete(conn.watches, parent)
		for _, watch := range watches {
			watch <- Event{EventNodeChildrenChanged, parent, nil}
		}
	}
}

func (conn *TestConnection) addwatch(p string) <-chan Event {
	eventC := make(chan Event, 1)
	conn.lock.Lock()
	watches := conn.watches[p]
	conn.watches[p] = append(watches, eventC)
	conn.lock.Unlock()
	return eventC
}

// Close implements Connection.Close
func (conn *TestConnection) Close() {
	var dirW, nodeW []string

	// Organize by child watch vs node watch
	for p := range conn.watches {
		if strings.HasSuffix(p, "/") {
			dirW = append(dirW, p)
		} else {
			nodeW = append(nodeW, p)
		}
	}

	// Signal the top-level path first and continue down the node tree
	sort.Strings(dirW)
	for _, d := range dirW {
		conn.updatewatch(d, EventSession)
	}

	// Signal all of the nodes
	for _, n := range nodeW {
		conn.updatewatch(n, EventSession)
	}
}

// SetID implements Connection.SetID
func (conn *TestConnection) SetID(id int) { conn.id = id }

// ID implements Connection.ID
func (conn *TestConnection) ID() int { return conn.id }

// SetOnClose implements Connection.SetOnClose
func (conn *TestConnection) SetOnClose(f func(int)) {}

// Create implements Connection.Create
func (conn *TestConnection) Create(p string, node Node) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	if node := conn.nodes[p]; node != nil {
		return ErrNodeExists
	} else if err := conn.CreateDir(p); err != nil {
		return err
	}

	data, err := json.Marshal(node)
	if err != nil {
		return err
	}
	conn.lock.Lock()
	conn.nodes[p] = data
	conn.lock.Unlock()
	return nil
}

// CreateDir implements Connection.CreateDir
func (conn *TestConnection) CreateDir(p string) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	if _, exists := conn.nodes[p]; exists {
		return nil
	} else if err := conn.CreateDir(path.Dir(p)); err != nil {
		return err
	}
	conn.lock.Lock()
	conn.nodes[p] = nil
	conn.lock.Unlock()
	conn.updatewatch(p, EventNodeCreated)
	return nil
}

// Exists implements Connection.Exists
func (conn *TestConnection) Exists(p string) (bool, error) {
	if err := conn.checkpath(&p); err != nil {
		return false, err
	}

	conn.lock.RLock()
	defer conn.lock.RUnlock()
	if _, exists := conn.nodes[p]; !exists {
		return false, ErrNoNode
	}

	return true, nil
}

// Delete implements Connection.Delete
func (conn *TestConnection) Delete(p string) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	children, _ := conn.Children(p)
	for _, c := range children {
		if err := conn.Delete(path.Join(p, c)); err != nil {
			return err
		}
	}
	if _, ok := conn.nodes[p]; ok {
		delete(conn.nodes, p)
	}
	conn.updatewatch(p, EventNodeDeleted)
	return nil
}

// ChildrenW implements Connection.ChildrenW
func (conn *TestConnection) ChildrenW(p string) ([]string, <-chan Event, error) {
	if err := conn.checkpath(&p); err != nil {
		return nil, nil, err
	}

	children, err := conn.Children(p)
	if err != nil {
		return nil, nil, err
	}

	return children, conn.addwatch(p), nil
}

// Children implements Connection.Children
func (conn *TestConnection) Children(p string) (children []string, err error) {
	if err := conn.checkpath(&p); err != nil {
		return nil, err
	}

	pattern := path.Join(p, "*")
	conn.lock.Lock()
	defer conn.lock.Unlock()
	for nodepath, _ := range conn.nodes {
		if match, _ := path.Match(pattern, nodepath); match {
			children = append(children, strings.TrimPrefix(nodepath, p+"/"))
		}
	}

	return children, nil
}

// GetW implements Connection.GetW
func (conn *TestConnection) GetW(p string, node Node) (<-chan Event, error) {
	if err := conn.checkpath(&p); err != nil {
		return nil, err
	}

	if err := conn.Get(p, node); err != nil {
		return nil, err
	}

	return conn.addwatch(p), nil
}

// Get implements Connection.Get
func (conn *TestConnection) Get(p string, node Node) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	conn.lock.RLock()
	defer conn.lock.RUnlock()
	if data, ok := conn.nodes[p]; !ok {
		return ErrNoNode
	} else if data == nil {
		return ErrEmptyNode
	} else if err := json.Unmarshal(data, node); err != nil {
		return err
	}
	return nil
}

// Set implements Connection.Set
func (conn *TestConnection) Set(p string, node Node) error {
	if err := conn.checkpath(&p); err != nil {
		return err
	}

	if _, ok := conn.nodes[p]; !ok {
		return ErrNoNode
	}

	data, err := json.Marshal(node)
	if err != nil {
		return err
	}

	// only update if something actually changed
	conn.lock.Lock()
	if bytes.Compare(conn.nodes[p], data) != 0 {
		conn.nodes[p] = data
		conn.updatewatch(p, EventNodeDataChanged)
	}
	conn.lock.Unlock()
	return nil
}

type TestLock struct {
}

func (l TestLock) Lock() error {
	return nil
}

func (l TestLock) Unlock() error {
	return nil
}

// NewLock implements Connection.NewLock
func (conn *TestConnection) NewLock(path string) Lock {
	return TestLock{}
}

// NewLeader implements Connection.NewLeader
func (conn *TestConnection) NewLeader(path string, data Node) Leader {
	return nil
}

// CreateEphemeral implements Connection.CreateEphemeral
func (conn *TestConnection) CreateEphemeral(path string, node Node) (string, error) {
	if err := conn.Create(path, node); err != nil {
		return "", err
	}
	return path, nil
}
<|endoftext|>


<commit_before>package file

import (
	"context"

	"github.com/coredns/coredns/core/dnsserver"
	"github.com/coredns/coredns/plugin/file/rrutil"
	"github.com/coredns/coredns/plugin/file/tree"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
)

// Result is the result of a Lookup
type Result int

const (
	// Success is a successful lookup.
	Success Result = iota
	// NameError indicates a nameerror
	NameError
	// Delegation indicates the lookup resulted in a delegation.
	Delegation
	// NoData indicates the lookup resulted in a NODATA.
	NoData
	// ServerFailure indicates a server failure during the lookup.
	ServerFailure
)

// Lookup looks up qname and qtype in the zone. When do is true DNSSEC records are included.
// Three sets of records are returned, one for the answer, one for authority  and one for the additional section.
func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) ([]dns.RR, []dns.RR, []dns.RR, Result) {
	qtype := state.QType()
	do := state.Do()

	// If z is a secondary zone we might not have transferred it, meaning we have
	// all zone context setup, except the actual record. This means (for one thing) the apex
	// is empty and we don't have a SOA record.
	z.RLock()
	ap := z.Apex
	tr := z.Tree
	z.RUnlock()
	if ap.SOA == nil {
		return nil, nil, nil, ServerFailure
	}

	if qname == z.origin {
		switch qtype {
		case dns.TypeSOA:
			return ap.soa(do), ap.ns(do), nil, Success
		case dns.TypeNS:
			nsrrs := ap.ns(do)
			glue := tr.Glue(nsrrs, do) // technically this isn't glue
			return nsrrs, nil, glue, Success
		}
	}

	var (
		found, shot    bool
		parts          string
		i              int
		elem, wildElem *tree.Elem
	)

	loop, _ := ctx.Value(dnsserver.LoopKey{}).(int)
	if loop > 8 {
		// We're back here for the 9th time; we have a loop and need to bail out.
		// Note the answer we're returning will be incomplete (more cnames to be followed) or
		// illegal (wildcard cname with multiple identical records). For now it's more important
		// to protect ourselves then to give the client a valid answer. We return with an error
		// to let the server handle what to do.
		return nil, nil, nil, ServerFailure
	}

	// Lookup:
	// * Per label from the right, look if it exists. We do this to find potential
	//   delegation records.
	// * If the per-label search finds nothing, we will look for the wildcard at the
	//   level. If found we keep it around. If we don't find the complete name we will
	//   use the wildcard.
	//
	// Main for-loop handles delegation and finding or not finding the qname.
	// If found we check if it is a CNAME/DNAME and do CNAME processing
	// We also check if we have type and do a nodata response.
	//
	// If not found, we check the potential wildcard, and use that for further processing.
	// If not found and no wildcard we will process this as an NXDOMAIN response.
	for {
		parts, shot = z.nameFromRight(qname, i)
		// We overshot the name, break and check if we previously found something.
		if shot {
			break
		}

		elem, found = tr.Search(parts)
		if !found {
			// Apex will always be found, when we are here we can search for a wildcard
			// and save the result of that search. So when nothing match, but we have a
			// wildcard we should expand the wildcard.

			wildcard := replaceWithAsteriskLabel(parts)
			if wild, found := tr.Search(wildcard); found {
				wildElem = wild
			}

			// Keep on searching, because maybe we hit an empty-non-terminal (which aren't
			// stored in the tree. Only when we have match the full qname (and possible wildcard
			// we can be confident that we didn't find anything.
			i++
			continue
		}

		// If we see DNAME records, we should return those.
		if dnamerrs := elem.Type(dns.TypeDNAME); dnamerrs != nil {
			// Only one DNAME is allowed per name. We just pick the first one to synthesize from.
			dname := dnamerrs[0]
			if cname := synthesizeCNAME(state.Name(), dname.(*dns.DNAME)); cname != nil {
				ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1)
				answer, ns, extra, rcode := z.externalLookup(ctx, state, elem, []dns.RR{cname})

				if do {
					sigs := elem.Type(dns.TypeRRSIG)
					sigs = rrutil.SubTypeSignature(sigs, dns.TypeDNAME)
					dnamerrs = append(dnamerrs, sigs...)
				}

				// The relevant DNAME RR should be included in the answer section,
				// if the DNAME is being employed as a substitution instruction.
				answer = append(dnamerrs, answer...)

				return answer, ns, extra, rcode
			}
			// The domain name that owns a DNAME record is allowed to have other RR types
			// at that domain name, except those have restrictions on what they can coexist
			// with (e.g. another DNAME). So there is nothing special left here.
		}

		// If we see NS records, it means the name as been delegated, and we should return the delegation.
		if nsrrs := elem.Type(dns.TypeNS); nsrrs != nil {

			// If the query is specifically for DS and the qname matches the delegated name, we should
			// return the DS in the answer section and leave the rest empty, i.e. just continue the loop
			// and continue searching.
			if qtype == dns.TypeDS && elem.Name() == qname {
				i++
				continue
			}

			glue := tr.Glue(nsrrs, do)
			if do {
				dss := typeFromElem(elem, dns.TypeDS, do)
				nsrrs = append(nsrrs, dss...)
			}

			return nil, nsrrs, glue, Delegation
		}

		i++
	}

	// What does found and !shot mean - do we ever hit it?
	if found && !shot {
		return nil, nil, nil, ServerFailure
	}

	// Found entire name.
	if found && shot {

		if rrs := elem.Type(dns.TypeCNAME); len(rrs) > 0 && qtype != dns.TypeCNAME {
			ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1)
			return z.externalLookup(ctx, state, elem, rrs)
		}

		rrs := elem.Type(qtype)

		// NODATA
		if len(rrs) == 0 {
			ret := ap.soa(do)
			if do {
				nsec := typeFromElem(elem, dns.TypeNSEC, do)
				ret = append(ret, nsec...)
			}
			return nil, ret, nil, NoData
		}

		// Additional section processing for MX, SRV. Check response and see if any of the names are in bailiwick -
		// if so add IP addresses to the additional section.
		additional := z.additionalProcessing(rrs, do)

		if do {
			sigs := elem.Type(dns.TypeRRSIG)
			sigs = rrutil.SubTypeSignature(sigs, qtype)
			rrs = append(rrs, sigs...)
		}

		return rrs, ap.ns(do), additional, Success

	}

	// Haven't found the original name.

	// Found wildcard.
	if wildElem != nil {
		auth := ap.ns(do)

		if rrs := wildElem.TypeForWildcard(dns.TypeCNAME, qname); len(rrs) > 0 {
			ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1)
			return z.externalLookup(ctx, state, wildElem, rrs)
		}

		rrs := wildElem.TypeForWildcard(qtype, qname)

		// NODATA response.
		if len(rrs) == 0 {
			ret := ap.soa(do)
			if do {
				nsec := typeFromElem(wildElem, dns.TypeNSEC, do)
				ret = append(ret, nsec...)
			}
			return nil, ret, nil, Success
		}

		if do {
			// An NSEC is needed to say no longer name exists under this wildcard.
			if deny, found := tr.Prev(qname); found {
				nsec := typeFromElem(deny, dns.TypeNSEC, do)
				auth = append(auth, nsec...)
			}

			sigs := wildElem.TypeForWildcard(dns.TypeRRSIG, qname)
			sigs = rrutil.SubTypeSignature(sigs, qtype)
			rrs = append(rrs, sigs...)

		}
		return rrs, auth, nil, Success
	}

	rcode := NameError

	// Hacky way to get around empty-non-terminals. If a longer name does exist, but this qname, does not, it
	// must be an empty-non-terminal. If so, we do the proper NXDOMAIN handling, but set the rcode to be success.
	if x, found := tr.Next(qname); found {
		if dns.IsSubDomain(qname, x.Name()) {
			rcode = Success
		}
	}

	ret := ap.soa(do)
	if do {
		deny, found := tr.Prev(qname)
		if !found {
			goto Out
		}
		nsec := typeFromElem(deny, dns.TypeNSEC, do)
		ret = append(ret, nsec...)

		if rcode != NameError {
			goto Out
		}

		ce, found := z.ClosestEncloser(qname)

		// wildcard denial only for NXDOMAIN
		if found {
			// wildcard denial
			wildcard := "*." + ce.Name()
			if ss, found := tr.Prev(wildcard); found {
				// Only add this nsec if it is different than the one already added
				if ss.Name() != deny.Name() {
					nsec := typeFromElem(ss, dns.TypeNSEC, do)
					ret = append(ret, nsec...)
				}
			}
		}

	}
Out:
	return nil, ret, nil, rcode
}

// typeFromElem returns the type tp from e and adds signatures (if they exist) and do is true.
func typeFromElem(elem *tree.Elem, tp uint16, do bool) []dns.RR {
	rrs := elem.Type(tp)
	if do {
		sigs := elem.Type(dns.TypeRRSIG)
		sigs = rrutil.SubTypeSignature(sigs, tp)
		rrs = append(rrs, sigs...)
	}
	return rrs
}

func (a Apex) soa(do bool) []dns.RR {
	if do {
		ret := append([]dns.RR{a.SOA}, a.SIGSOA...)
		return ret
	}
	return []dns.RR{a.SOA}
}

func (a Apex) ns(do bool) []dns.RR {
	if do {
		ret := append(a.NS, a.SIGNS...)
		return ret
	}
	return a.NS
}

// externalLookup adds signatures and tries to resolve CNAMEs that point to external names.
func (z *Zone) externalLookup(ctx context.Context, state request.Request, elem *tree.Elem, rrs []dns.RR) ([]dns.RR, []dns.RR, []dns.RR, Result) {

	qtype := state.QType()
	do := state.Do()

	if do {
		sigs := elem.Type(dns.TypeRRSIG)
		sigs = rrutil.SubTypeSignature(sigs, dns.TypeCNAME)
		rrs = append(rrs, sigs...)
	}

	targetName := rrs[0].(*dns.CNAME).Target
	elem, _ = z.Tree.Search(targetName)
	if elem == nil {
		lookupRRs, result := z.doLookup(ctx, state, targetName, qtype)
		rrs = append(rrs, lookupRRs...)
		return rrs, z.Apex.ns(do), nil, result
	}

	i := 0

Redo:
	cname := elem.Type(dns.TypeCNAME)
	if len(cname) > 0 {
		rrs = append(rrs, cname...)

		if do {
			sigs := elem.Type(dns.TypeRRSIG)
			sigs = rrutil.SubTypeSignature(sigs, dns.TypeCNAME)
			rrs = append(rrs, sigs...)
		}
		targetName := cname[0].(*dns.CNAME).Target
		elem, _ = z.Tree.Search(targetName)
		if elem == nil {
			lookupRRs, result := z.doLookup(ctx, state, targetName, qtype)
			rrs = append(rrs, lookupRRs...)
			return rrs, z.Apex.ns(do), nil, result
		}

		i++
		if i > 8 {
			return rrs, z.Apex.ns(do), nil, Success
		}

		goto Redo
	}

	targets := rrutil.CNAMEForType(elem.All(), qtype)
	if len(targets) > 0 {
		rrs = append(rrs, targets...)

		if do {
			sigs := elem.Type(dns.TypeRRSIG)
			sigs = rrutil.SubTypeSignature(sigs, qtype)
			rrs = append(rrs, sigs...)
		}
	}

	return rrs, z.Apex.ns(do), nil, Success
}

func (z *Zone) doLookup(ctx context.Context, state request.Request, target string, qtype uint16) ([]dns.RR, Result) {
	m, e := z.Upstream.Lookup(ctx, state, target, qtype)
	if e != nil {
		return nil, Success
	}
	if m == nil {
		return nil, Success
	}
	if m.Rcode == dns.RcodeNameError {
		return m.Answer, NameError
	}
	if m.Rcode == dns.RcodeServerFailure {
		return m.Answer, ServerFailure
	}
	if m.Rcode == dns.RcodeSuccess && len(m.Answer) == 0 {
		return m.Answer, NoData
	}
	return m.Answer, Success
}

// additionalProcessing checks the current answer section and retrieves A or AAAA records
// (and possible SIGs) to need to be put in the additional section.
func (z *Zone) additionalProcessing(answer []dns.RR, do bool) (extra []dns.RR) {
	for _, rr := range answer {
		name := ""
		switch x := rr.(type) {
		case *dns.SRV:
			name = x.Target
		case *dns.MX:
			name = x.Mx
		}
		if len(name) == 0 || !dns.IsSubDomain(z.origin, name) {
			continue
		}

		elem, _ := z.Tree.Search(name)
		if elem == nil {
			continue
		}

		sigs := elem.Type(dns.TypeRRSIG)
		for _, addr := range []uint16{dns.TypeA, dns.TypeAAAA} {
			if a := elem.Type(addr); a != nil {
				extra = append(extra, a...)
				if do {
					sig := rrutil.SubTypeSignature(sigs, addr)
					extra = append(extra, sig...)
				}
			}
		}
	}

	return extra
}
<commit_msg>make copies of RRs before returning them (#4409)<commit_after>package file

import (
	"context"

	"github.com/coredns/coredns/core/dnsserver"
	"github.com/coredns/coredns/plugin/file/rrutil"
	"github.com/coredns/coredns/plugin/file/tree"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
)

// Result is the result of a Lookup
type Result int

const (
	// Success is a successful lookup.
	Success Result = iota
	// NameError indicates a nameerror
	NameError
	// Delegation indicates the lookup resulted in a delegation.
	Delegation
	// NoData indicates the lookup resulted in a NODATA.
	NoData
	// ServerFailure indicates a server failure during the lookup.
	ServerFailure
)

// Lookup looks up qname and qtype in the zone. When do is true DNSSEC records are included.
// Three sets of records are returned, one for the answer, one for authority  and one for the additional section.
func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) ([]dns.RR, []dns.RR, []dns.RR, Result) {
	qtype := state.QType()
	do := state.Do()

	// If z is a secondary zone we might not have transferred it, meaning we have
	// all zone context setup, except the actual record. This means (for one thing) the apex
	// is empty and we don't have a SOA record.
	z.RLock()
	ap := z.Apex
	tr := z.Tree
	z.RUnlock()
	if ap.SOA == nil {
		return nil, nil, nil, ServerFailure
	}

	if qname == z.origin {
		switch qtype {
		case dns.TypeSOA:
			return ap.soa(do), ap.ns(do), nil, Success
		case dns.TypeNS:
			nsrrs := ap.ns(do)
			glue := tr.Glue(nsrrs, do) // technically this isn't glue
			return nsrrs, nil, glue, Success
		}
	}

	var (
		found, shot    bool
		parts          string
		i              int
		elem, wildElem *tree.Elem
	)

	loop, _ := ctx.Value(dnsserver.LoopKey{}).(int)
	if loop > 8 {
		// We're back here for the 9th time; we have a loop and need to bail out.
		// Note the answer we're returning will be incomplete (more cnames to be followed) or
		// illegal (wildcard cname with multiple identical records). For now it's more important
		// to protect ourselves then to give the client a valid answer. We return with an error
		// to let the server handle what to do.
		return nil, nil, nil, ServerFailure
	}

	// Lookup:
	// * Per label from the right, look if it exists. We do this to find potential
	//   delegation records.
	// * If the per-label search finds nothing, we will look for the wildcard at the
	//   level. If found we keep it around. If we don't find the complete name we will
	//   use the wildcard.
	//
	// Main for-loop handles delegation and finding or not finding the qname.
	// If found we check if it is a CNAME/DNAME and do CNAME processing
	// We also check if we have type and do a nodata response.
	//
	// If not found, we check the potential wildcard, and use that for further processing.
	// If not found and no wildcard we will process this as an NXDOMAIN response.
	for {
		parts, shot = z.nameFromRight(qname, i)
		// We overshot the name, break and check if we previously found something.
		if shot {
			break
		}

		elem, found = tr.Search(parts)
		if !found {
			// Apex will always be found, when we are here we can search for a wildcard
			// and save the result of that search. So when nothing match, but we have a
			// wildcard we should expand the wildcard.

			wildcard := replaceWithAsteriskLabel(parts)
			if wild, found := tr.Search(wildcard); found {
				wildElem = wild
			}

			// Keep on searching, because maybe we hit an empty-non-terminal (which aren't
			// stored in the tree. Only when we have match the full qname (and possible wildcard
			// we can be confident that we didn't find anything.
			i++
			continue
		}

		// If we see DNAME records, we should return those.
		if dnamerrs := elem.Type(dns.TypeDNAME); dnamerrs != nil {
			// Only one DNAME is allowed per name. We just pick the first one to synthesize from.
			dname := dnamerrs[0]
			if cname := synthesizeCNAME(state.Name(), dname.(*dns.DNAME)); cname != nil {
				ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1)
				answer, ns, extra, rcode := z.externalLookup(ctx, state, elem, []dns.RR{cname})

				if do {
					sigs := elem.Type(dns.TypeRRSIG)
					sigs = rrutil.SubTypeSignature(sigs, dns.TypeDNAME)
					dnamerrs = append(dnamerrs, sigs...)
				}

				// The relevant DNAME RR should be included in the answer section,
				// if the DNAME is being employed as a substitution instruction.
				answer = append(dnamerrs, answer...)

				return answer, ns, extra, rcode
			}
			// The domain name that owns a DNAME record is allowed to have other RR types
			// at that domain name, except those have restrictions on what they can coexist
			// with (e.g. another DNAME). So there is nothing special left here.
		}

		// If we see NS records, it means the name as been delegated, and we should return the delegation.
		if nsrrs := elem.Type(dns.TypeNS); nsrrs != nil {

			// If the query is specifically for DS and the qname matches the delegated name, we should
			// return the DS in the answer section and leave the rest empty, i.e. just continue the loop
			// and continue searching.
			if qtype == dns.TypeDS && elem.Name() == qname {
				i++
				continue
			}

			glue := tr.Glue(nsrrs, do)
			if do {
				dss := typeFromElem(elem, dns.TypeDS, do)
				nsrrs = append(nsrrs, dss...)
			}

			return nil, nsrrs, glue, Delegation
		}

		i++
	}

	// What does found and !shot mean - do we ever hit it?
	if found && !shot {
		return nil, nil, nil, ServerFailure
	}

	// Found entire name.
	if found && shot {

		if rrs := elem.Type(dns.TypeCNAME); len(rrs) > 0 && qtype != dns.TypeCNAME {
			ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1)
			return z.externalLookup(ctx, state, elem, rrs)
		}

		treeRRs := elem.Type(qtype)
		// make a copy of the element RRs to prevent response writers from mutating the tree
		rrs := make([]dns.RR, len(treeRRs))
		for i, rr := range treeRRs {
			rrs[i] = dns.Copy(rr)
		}

		// NODATA
		if len(rrs) == 0 {
			ret := ap.soa(do)
			if do {
				nsec := typeFromElem(elem, dns.TypeNSEC, do)
				ret = append(ret, nsec...)
			}
			return nil, ret, nil, NoData
		}

		// Additional section processing for MX, SRV. Check response and see if any of the names are in bailiwick -
		// if so add IP addresses to the additional section.
		additional := z.additionalProcessing(rrs, do)

		if do {
			sigs := elem.Type(dns.TypeRRSIG)
			sigs = rrutil.SubTypeSignature(sigs, qtype)
			rrs = append(rrs, sigs...)
		}

		return rrs, ap.ns(do), additional, Success

	}

	// Haven't found the original name.

	// Found wildcard.
	if wildElem != nil {
		auth := ap.ns(do)

		if rrs := wildElem.TypeForWildcard(dns.TypeCNAME, qname); len(rrs) > 0 {
			ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1)
			return z.externalLookup(ctx, state, wildElem, rrs)
		}

		rrs := wildElem.TypeForWildcard(qtype, qname)

		// NODATA response.
		if len(rrs) == 0 {
			ret := ap.soa(do)
			if do {
				nsec := typeFromElem(wildElem, dns.TypeNSEC, do)
				ret = append(ret, nsec...)
			}
			return nil, ret, nil, Success
		}

		if do {
			// An NSEC is needed to say no longer name exists under this wildcard.
			if deny, found := tr.Prev(qname); found {
				nsec := typeFromElem(deny, dns.TypeNSEC, do)
				auth = append(auth, nsec...)
			}

			sigs := wildElem.TypeForWildcard(dns.TypeRRSIG, qname)
			sigs = rrutil.SubTypeSignature(sigs, qtype)
			rrs = append(rrs, sigs...)

		}
		return rrs, auth, nil, Success
	}

	rcode := NameError

	// Hacky way to get around empty-non-terminals. If a longer name does exist, but this qname, does not, it
	// must be an empty-non-terminal. If so, we do the proper NXDOMAIN handling, but set the rcode to be success.
	if x, found := tr.Next(qname); found {
		if dns.IsSubDomain(qname, x.Name()) {
			rcode = Success
		}
	}

	ret := ap.soa(do)
	if do {
		deny, found := tr.Prev(qname)
		if !found {
			goto Out
		}
		nsec := typeFromElem(deny, dns.TypeNSEC, do)
		ret = append(ret, nsec...)

		if rcode != NameError {
			goto Out
		}

		ce, found := z.ClosestEncloser(qname)

		// wildcard denial only for NXDOMAIN
		if found {
			// wildcard denial
			wildcard := "*." + ce.Name()
			if ss, found := tr.Prev(wildcard); found {
				// Only add this nsec if it is different than the one already added
				if ss.Name() != deny.Name() {
					nsec := typeFromElem(ss, dns.TypeNSEC, do)
					ret = append(ret, nsec...)
				}
			}
		}

	}
Out:
	return nil, ret, nil, rcode
}

// typeFromElem returns the type tp from e and adds signatures (if they exist) and do is true.
func typeFromElem(elem *tree.Elem, tp uint16, do bool) []dns.RR {
	rrs := elem.Type(tp)
	if do {
		sigs := elem.Type(dns.TypeRRSIG)
		sigs = rrutil.SubTypeSignature(sigs, tp)
		rrs = append(rrs, sigs...)
	}
	return rrs
}

func (a Apex) soa(do bool) []dns.RR {
	if do {
		ret := append([]dns.RR{a.SOA}, a.SIGSOA...)
		return ret
	}
	return []dns.RR{a.SOA}
}

func (a Apex) ns(do bool) []dns.RR {
	if do {
		ret := append(a.NS, a.SIGNS...)
		return ret
	}
	return a.NS
}

// externalLookup adds signatures and tries to resolve CNAMEs that point to external names.
func (z *Zone) externalLookup(ctx context.Context, state request.Request, elem *tree.Elem, rrs []dns.RR) ([]dns.RR, []dns.RR, []dns.RR, Result) {

	qtype := state.QType()
	do := state.Do()

	if do {
		sigs := elem.Type(dns.TypeRRSIG)
		sigs = rrutil.SubTypeSignature(sigs, dns.TypeCNAME)
		rrs = append(rrs, sigs...)
	}

	targetName := rrs[0].(*dns.CNAME).Target
	elem, _ = z.Tree.Search(targetName)
	if elem == nil {
		lookupRRs, result := z.doLookup(ctx, state, targetName, qtype)
		rrs = append(rrs, lookupRRs...)
		return rrs, z.Apex.ns(do), nil, result
	}

	i := 0

Redo:
	cname := elem.Type(dns.TypeCNAME)
	if len(cname) > 0 {
		rrs = append(rrs, cname...)

		if do {
			sigs := elem.Type(dns.TypeRRSIG)
			sigs = rrutil.SubTypeSignature(sigs, dns.TypeCNAME)
			rrs = append(rrs, sigs...)
		}
		targetName := cname[0].(*dns.CNAME).Target
		elem, _ = z.Tree.Search(targetName)
		if elem == nil {
			lookupRRs, result := z.doLookup(ctx, state, targetName, qtype)
			rrs = append(rrs, lookupRRs...)
			return rrs, z.Apex.ns(do), nil, result
		}

		i++
		if i > 8 {
			return rrs, z.Apex.ns(do), nil, Success
		}

		goto Redo
	}

	targets := rrutil.CNAMEForType(elem.All(), qtype)
	if len(targets) > 0 {
		rrs = append(rrs, targets...)

		if do {
			sigs := elem.Type(dns.TypeRRSIG)
			sigs = rrutil.SubTypeSignature(sigs, qtype)
			rrs = append(rrs, sigs...)
		}
	}

	return rrs, z.Apex.ns(do), nil, Success
}

func (z *Zone) doLookup(ctx context.Context, state request.Request, target string, qtype uint16) ([]dns.RR, Result) {
	m, e := z.Upstream.Lookup(ctx, state, target, qtype)
	if e != nil {
		return nil, Success
	}
	if m == nil {
		return nil, Success
	}
	if m.Rcode == dns.RcodeNameError {
		return m.Answer, NameError
	}
	if m.Rcode == dns.RcodeServerFailure {
		return m.Answer, ServerFailure
	}
	if m.Rcode == dns.RcodeSuccess && len(m.Answer) == 0 {
		return m.Answer, NoData
	}
	return m.Answer, Success
}

// additionalProcessing checks the current answer section and retrieves A or AAAA records
// (and possible SIGs) to need to be put in the additional section.
func (z *Zone) additionalProcessing(answer []dns.RR, do bool) (extra []dns.RR) {
	for _, rr := range answer {
		name := ""
		switch x := rr.(type) {
		case *dns.SRV:
			name = x.Target
		case *dns.MX:
			name = x.Mx
		}
		if len(name) == 0 || !dns.IsSubDomain(z.origin, name) {
			continue
		}

		elem, _ := z.Tree.Search(name)
		if elem == nil {
			continue
		}

		sigs := elem.Type(dns.TypeRRSIG)
		for _, addr := range []uint16{dns.TypeA, dns.TypeAAAA} {
			if a := elem.Type(addr); a != nil {
				extra = append(extra, a...)
				if do {
					sig := rrutil.SubTypeSignature(sigs, addr)
					extra = append(extra, sig...)
				}
			}
		}
	}

	return extra
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"time"

	"github.com/containerops/wharf/utils"
)

type Compose struct {
	Id            string   `json:"id"`            //
	Compose       string   `json:"compose"`       //
	Namespace     string   `json:"namespace"`     //
	NamespaceType bool     `json:"namespacetype"` //
	Organization  string   `json:"organization"`  //
	Tags          []string `json:"tags"`          //
	Starts        []string `json:"starts"`        //
	Comments      []string `json:"comments"`      //
	Short         string   `json:"short"`         //
	Description   string   `json:"description"`   //
	YAML          string   `json:"yaml"`          //
	Download      int64    `json:"download"`      //
	Icon          string   `json:"icon"`          //
	Privated      bool     `json:"privated"`      //
	Collaborators []string `json:"collaborators"` //
	Permissions   []string `json:"permissions"`   //
	Created       int64    `json:"created"`       //
	Updated       int64    `json:"updated"`       //
	Memo          []string `json:"memo"`          //
}

func (c *Compose) Has(namespace, compose string) (bool, []byte, error) {
	id, err := GetByGobalId("compose", fmt.Sprintf("%s:%s", namespace, compose))

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

	if len(id) <= 0 {
		return false, nil, nil
	}

	err = Get(c, id)

	return true, id, err
}

func (c *Compose) Save() error {
	if err := Save(c, []byte(c.Id)); err != nil {
		return err
	}

	if _, err := LedisDB.HSet([]byte(GLOBAL_COMPOSE_INDEX), []byte(fmt.Sprintf("%s:%s", c.Namespace, c.Compose)), []byte(c.Id)); err != nil {
		return err
	}

	return nil
}

func (compose *Compose) Log(action, level, t int64, actionId string, content []byte) error {
	log := Log{Action: action, ActionId: actionId, Level: level, Type: t, Content: string(content), Created: time.Now().UnixNano() / int64(time.Millisecond)}
	log.Id = string(utils.GeneralKey(actionId))

	if err := log.Save(); err != nil {
		return err
	}

	compose.Memo = append(compose.Memo, log.Id)

	if err := compose.Save(); err != nil {
		return err
	}

	return nil
}
<commit_msg>Remove NamespaceType<commit_after>package models

import (
	"fmt"
	"time"

	"github.com/containerops/wharf/utils"
)

type Compose struct {
	Id            string   `json:"id"`            //
	Compose       string   `json:"compose"`       //
	Namespace     string   `json:"namespace"`     //
	Organization  string   `json:"organization"`  //
	Tags          []string `json:"tags"`          //
	Starts        []string `json:"starts"`        //
	Comments      []string `json:"comments"`      //
	Short         string   `json:"short"`         //
	Description   string   `json:"description"`   //
	YAML          string   `json:"yaml"`          //
	Download      int64    `json:"download"`      //
	Icon          string   `json:"icon"`          //
	Privated      bool     `json:"privated"`      //
	Collaborators []string `json:"collaborators"` //
	Permissions   []string `json:"permissions"`   //
	Created       int64    `json:"created"`       //
	Updated       int64    `json:"updated"`       //
	Memo          []string `json:"memo"`          //
}

func (c *Compose) Has(namespace, compose string) (bool, []byte, error) {
	id, err := GetByGobalId("compose", fmt.Sprintf("%s:%s", namespace, compose))

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

	if len(id) <= 0 {
		return false, nil, nil
	}

	err = Get(c, id)

	return true, id, err
}

func (c *Compose) Save() error {
	if err := Save(c, []byte(c.Id)); err != nil {
		return err
	}

	if _, err := LedisDB.HSet([]byte(GLOBAL_COMPOSE_INDEX), []byte(fmt.Sprintf("%s:%s", c.Namespace, c.Compose)), []byte(c.Id)); err != nil {
		return err
	}

	return nil
}

func (compose *Compose) Log(action, level, t int64, actionId string, content []byte) error {
	log := Log{Action: action, ActionId: actionId, Level: level, Type: t, Content: string(content), Created: time.Now().UnixNano() / int64(time.Millisecond)}
	log.Id = string(utils.GeneralKey(actionId))

	if err := log.Save(); err != nil {
		return err
	}

	compose.Memo = append(compose.Memo, log.Id)

	if err := compose.Save(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package plugin

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/labstack/echo"
	"github.com/stretchr/testify/assert"
)

func TestHeader(t *testing.T) {
	e := echo.New()
	req := httptest.NewRequest(echo.GET, "/", nil)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	ok := func(c echo.Context) error {
		return c.String(http.StatusOK, "OK")
	}
	h := new(Header)
	h.Set = map[string]string{"Name": "Jon"}
	h.Add = map[string]string{"Name": "Joe"}
	h.Del = []string{"Delete"}
	rec.Header().Set("Delete", "me")

	h.Initialize()
	h.Process(ok)(c)

	assert.Equal(t, "Jon", rec.Header().Get("Name"))                    // Set
	assert.EqualValues(t, []string{"Jon", "Joe"}, rec.Header()["Name"]) // Add
	assert.Equal(t, "", rec.Header().Get("Delete"))                     // Del
}
<commit_msg>Fixed test<commit_after>package plugin

import (
	"net/http"
	"net/http/httptest"
	"sync"
	"testing"

	"github.com/labstack/echo"
	"github.com/stretchr/testify/assert"
)

func TestHeader(t *testing.T) {
	e := echo.New()
	req := httptest.NewRequest(echo.GET, "/", nil)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)
	ok := func(c echo.Context) error {
		return c.String(http.StatusOK, "OK")
	}
	h := new(Header)
	h.Base = Base{mutex: new(sync.RWMutex)}
	h.Set = map[string]string{"Name": "Jon"}
	h.Add = map[string]string{"Name": "Joe"}
	h.Del = []string{"Delete"}
	rec.Header().Set("Delete", "me")

	h.Initialize()
	h.Process(ok)(c)

	assert.Equal(t, "Jon", rec.Header().Get("Name"))                    // Set
	assert.EqualValues(t, []string{"Jon", "Joe"}, rec.Header()["Name"]) // Add
	assert.Equal(t, "", rec.Header().Get("Delete"))                     // Del
}
<|endoftext|>


<commit_before>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handler

import (
	"testing"

	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	pb_protocol "github.com/TheThingsNetwork/ttn/api/protocol"
	pb_lorawan "github.com/TheThingsNetwork/ttn/api/protocol/lorawan"
	"github.com/TheThingsNetwork/ttn/core/component"
	"github.com/TheThingsNetwork/ttn/core/handler/device"
	"github.com/TheThingsNetwork/ttn/core/types"
	. "github.com/TheThingsNetwork/ttn/utils/testing"
	. "github.com/smartystreets/assertions"
)

func buildLorawanUplink(payload []byte) (*pb_broker.DeduplicatedUplinkMessage, *types.UplinkMessage) {
	ttnUp := &pb_broker.DeduplicatedUplinkMessage{
		DevId:   "devid",
		AppId:   "appid",
		Payload: payload,
		ProtocolMetadata: &pb_protocol.RxMetadata{Protocol: &pb_protocol.RxMetadata_Lorawan{
			Lorawan: &pb_lorawan.Metadata{
				FCnt: 1,
			},
		}},
	}
	appUp := &types.UplinkMessage{}
	return ttnUp, appUp
}

func TestConvertFromLoRaWAN(t *testing.T) {
	a := New(t)
	h := &handler{
		Component: &component.Component{Ctx: GetLogger(t, "TestConvertFromLoRaWAN")},
		devices:   device.NewRedisDeviceStore(GetRedisClient(), "handler-test-convert-from-lorawan"),
		qEvent:    make(chan *types.DeviceEvent, 10),
	}
	device := &device.Device{
		DevID: "devid",
		AppID: "appid",
	}
	ttnUp, appUp := buildLorawanUplink([]byte{0x40, 0x04, 0x03, 0x02, 0x01, 0x20, 0x01, 0x00, 0x0A, 0x46, 0x55, 0x96, 0x42, 0x92, 0xF2})
	err := h.ConvertFromLoRaWAN(h.Ctx, ttnUp, appUp, device)
	a.So(err, ShouldBeNil)
	a.So(appUp.PayloadRaw, ShouldResemble, []byte{0xaa, 0xbc})
	a.So(appUp.FCnt, ShouldEqual, 1)

	ttnUp.UnmarshalPayload()
	ttnUp.Message.GetLorawan().MType = pb_lorawan.MType_CONFIRMED_UP
	ttnUp.Message.GetLorawan().SetMIC(types.NwkSKey([16]byte{}))
	ttnUp.Payload = ttnUp.Message.GetLorawan().PHYPayloadBytes()

	err = h.ConvertFromLoRaWAN(h.Ctx, ttnUp, appUp, device)
	a.So(err, ShouldBeNil)
	a.So(appUp.Confirmed, ShouldBeTrue)

}

func buildLorawanDownlink(payload []byte) (*types.DownlinkMessage, *pb_broker.DownlinkMessage) {
	appDown := &types.DownlinkMessage{
		DevID:      "devid",
		AppID:      "appid",
		PayloadRaw: []byte{0xaa, 0xbc},
	}
	ttnDown := &pb_broker.DownlinkMessage{
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
		DownlinkOption: &pb_broker.DownlinkOption{
			ProtocolConfig: &pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_Lorawan{
				Lorawan: &pb_lorawan.TxConfiguration{
					FCnt: 1,
				},
			}},
		},
	}
	return appDown, ttnDown
}

func TestConvertToLoRaWAN(t *testing.T) {
	a := New(t)
	h := &handler{
		Component: &component.Component{Ctx: GetLogger(t, "TestConvertToLoRaWAN")},
		devices:   device.NewRedisDeviceStore(GetRedisClient(), "handler-test-convert-to-lorawan"),
	}
	device := &device.Device{
		DevID: "devid",
		AppID: "appid",
	}
	appDown, ttnDown := buildLorawanDownlink([]byte{0xaa, 0xbc})
	err := h.ConvertToLoRaWAN(h.Ctx, appDown, ttnDown, device)
	a.So(err, ShouldBeNil)
	a.So(ttnDown.Payload, ShouldResemble, []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0xa1, 0x33, 0x68, 0x0A, 0x08, 0xBD})

	appDown, ttnDown = buildLorawanDownlink([]byte{0xaa, 0xbc})
	appDown.FPort = 8
	err = h.ConvertToLoRaWAN(h.Ctx, appDown, ttnDown, device)
	a.So(err, ShouldBeNil)
	a.So(ttnDown.Payload, ShouldResemble, []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x08, 0xa1, 0x33, 0x41, 0xA9, 0xFA, 0x03})
}
<commit_msg>Add extra tests for Confirmed downlinks<commit_after>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handler

import (
	"testing"

	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	pb_protocol "github.com/TheThingsNetwork/ttn/api/protocol"
	pb_lorawan "github.com/TheThingsNetwork/ttn/api/protocol/lorawan"
	"github.com/TheThingsNetwork/ttn/core/component"
	"github.com/TheThingsNetwork/ttn/core/handler/device"
	"github.com/TheThingsNetwork/ttn/core/types"
	. "github.com/TheThingsNetwork/ttn/utils/testing"
	. "github.com/smartystreets/assertions"
)

func buildLorawanUplink(payload []byte) (*pb_broker.DeduplicatedUplinkMessage, *types.UplinkMessage) {
	ttnUp := &pb_broker.DeduplicatedUplinkMessage{
		DevId:   "devid",
		AppId:   "appid",
		Payload: payload,
		ProtocolMetadata: &pb_protocol.RxMetadata{Protocol: &pb_protocol.RxMetadata_Lorawan{
			Lorawan: &pb_lorawan.Metadata{
				FCnt: 1,
			},
		}},
	}
	appUp := &types.UplinkMessage{}
	return ttnUp, appUp
}

func TestConvertFromLoRaWAN(t *testing.T) {
	a := New(t)
	var wg WaitGroup
	h := &handler{
		Component: &component.Component{Ctx: GetLogger(t, "TestConvertFromLoRaWAN")},
		devices:   device.NewRedisDeviceStore(GetRedisClient(), "handler-test-convert-from-lorawan"),
		qEvent:    make(chan *types.DeviceEvent, 10),
	}
	device := &device.Device{
		DevID:           "devid",
		AppID:           "appid",
		CurrentDownlink: &types.DownlinkMessage{},
	}
	ttnUp, appUp := buildLorawanUplink([]byte{0x40, 0x04, 0x03, 0x02, 0x01, 0x20, 0x01, 0x00, 0x0A, 0x46, 0x55, 0x96, 0x42, 0x92, 0xF2})
	err := h.ConvertFromLoRaWAN(h.Ctx, ttnUp, appUp, device)
	a.So(err, ShouldBeNil)
	a.So(appUp.PayloadRaw, ShouldResemble, []byte{0xaa, 0xbc})
	a.So(appUp.FCnt, ShouldEqual, 1)
	a.So(device.CurrentDownlink, ShouldBeNil)

	device.CurrentDownlink = &types.DownlinkMessage{Confirmed: true}

	ttnUp.UnmarshalPayload()
	ttnUp.Message.GetLorawan().MType = pb_lorawan.MType_CONFIRMED_UP
	ttnUp.Message.GetLorawan().GetMacPayload().FCnt++
	ttnUp.GetProtocolMetadata().GetLorawan().FCnt = ttnUp.Message.GetLorawan().GetMacPayload().FCnt
	ttnUp.Message.GetLorawan().GetMacPayload().Ack = false
	ttnUp.Message.GetLorawan().SetMIC(device.NwkSKey)
	ttnUp.Payload = ttnUp.Message.GetLorawan().PHYPayloadBytes()

	err = h.ConvertFromLoRaWAN(h.Ctx, ttnUp, appUp, device)
	a.So(err, ShouldBeNil)
	a.So(appUp.Confirmed, ShouldBeTrue)
	a.So(device.CurrentDownlink, ShouldNotBeNil)

	device.CurrentDownlink = &types.DownlinkMessage{Confirmed: true}

	wg.Add(1)
	go func() {
		<-h.qEvent
		wg.Done()
	}()

	ttnUp.UnmarshalPayload()
	ttnUp.Message.GetLorawan().MType = pb_lorawan.MType_CONFIRMED_UP
	ttnUp.Message.GetLorawan().GetMacPayload().FCnt++
	ttnUp.GetProtocolMetadata().GetLorawan().FCnt = ttnUp.Message.GetLorawan().GetMacPayload().FCnt
	ttnUp.Message.GetLorawan().GetMacPayload().Ack = true
	ttnUp.Message.GetLorawan().SetMIC(device.NwkSKey)
	ttnUp.Payload = ttnUp.Message.GetLorawan().PHYPayloadBytes()

	err = h.ConvertFromLoRaWAN(h.Ctx, ttnUp, appUp, device)
	a.So(err, ShouldBeNil)
	a.So(appUp.Confirmed, ShouldBeTrue)

	wg.Wait()
}

func buildLorawanDownlink(payload []byte) (*types.DownlinkMessage, *pb_broker.DownlinkMessage) {
	appDown := &types.DownlinkMessage{
		DevID:      "devid",
		AppID:      "appid",
		PayloadRaw: []byte{0xaa, 0xbc},
	}
	ttnDown := &pb_broker.DownlinkMessage{
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
		DownlinkOption: &pb_broker.DownlinkOption{
			ProtocolConfig: &pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_Lorawan{
				Lorawan: &pb_lorawan.TxConfiguration{
					FCnt: 1,
				},
			}},
		},
	}
	return appDown, ttnDown
}

func TestConvertToLoRaWAN(t *testing.T) {
	a := New(t)
	h := &handler{
		Component: &component.Component{Ctx: GetLogger(t, "TestConvertToLoRaWAN")},
		devices:   device.NewRedisDeviceStore(GetRedisClient(), "handler-test-convert-to-lorawan"),
	}
	device := &device.Device{
		DevID: "devid",
		AppID: "appid",
	}
	appDown, ttnDown := buildLorawanDownlink([]byte{0xaa, 0xbc})
	err := h.ConvertToLoRaWAN(h.Ctx, appDown, ttnDown, device)
	a.So(err, ShouldBeNil)
	a.So(ttnDown.Payload, ShouldResemble, []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0xa1, 0x33, 0x68, 0x0A, 0x08, 0xBD})

	appDown, ttnDown = buildLorawanDownlink([]byte{0xaa, 0xbc})
	appDown.FPort = 8
	err = h.ConvertToLoRaWAN(h.Ctx, appDown, ttnDown, device)
	a.So(err, ShouldBeNil)
	a.So(ttnDown.Payload, ShouldResemble, []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x08, 0xa1, 0x33, 0x41, 0xA9, 0xFA, 0x03})
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"strings"
)

type ParseError struct {
	// starts with 1
	Line   uint
	Column uint

	Msg string

	Src string
}

func (p *ParseError) Error() string {
	return fmt.Sprintf("%d:%d: %s", p.Line, p.Column, p.Msg)
}

func (p *ParseError) Verbose() string {
	l := fmt.Sprintf("%d: ", p.Line)
	return p.Error() + "\n\n" +
		"\033[36m" + l + "\033[0m" + strings.Split(p.Src, "\n")[p.Line-1] + "\n" +
		strings.Repeat(" ", int(p.Column-1)+len(l)) + "\033[1m" + "^ error occurs" + "\033[0m"
}
<commit_msg>Refactor Verbose method<commit_after>package parser

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/elpinal/color"
)

type ParseError struct {
	// starts with 1
	Line   uint
	Column uint

	Msg string

	Src string
}

func (p *ParseError) Error() string {
	return fmt.Sprintf("%d:%d: %s", p.Line, p.Column, p.Msg)
}

func (p *ParseError) Verbose() string {
	var buf bytes.Buffer
	buf.WriteString(p.Error())
	buf.WriteString("\n\n")

	l := fmt.Sprintf("%d: ", p.Line)
	buf.WriteString(color.Wrap(l, color.Cyan))
	buf.WriteString(strings.Split(p.Src, "\n")[p.Line-1])
	buf.WriteByte('\n')

	buf.WriteString(strings.Repeat(" ", int(p.Column-1)+len(l)))
	buf.WriteString("\033[1m^ error occurs\033[0m")
	return buf.String()
}
<|endoftext|>


<commit_before>package goshopify

import (
	"fmt"
	"time"

	"github.com/shopspring/decimal"
)

const customersBasePath = "admin/customers"

// CustomerService is an interface for interfacing with the customers endpoints
// of the Shopify API.
// See: https://help.shopify.com/api/reference/customer
type CustomerService interface {
	List(interface{}) ([]Customer, error)
	Count(interface{}) (int, error)
	Get(int, interface{}) (*Customer, error)
	Search(interface{}) ([]Customer, error)
	Create(Customer) (*Customer, error)
	Update(Customer) (*Customer, error)
}

// CustomerServiceOp handles communication with the product related methods of
// the Shopify API.
type CustomerServiceOp struct {
	client *Client
}

// Customer represents a Shopify customer
type Customer struct {
	ID                  int              `json:"id"`
	Email               string           `json:"email"`
	FirstName           string           `json:"first_name"`
	LastName            string           `json:"last_name"`
	State               string           `json:"state"`
	Note                string           `json:"note"`
	VerifiedEmail       bool             `json:"verified_email"`
	MultipassIdentifier string           `json:"multipass_identifier"`
	OrdersCount         int              `json:"orders_count"`
	TaxExempt           bool             `json:"tax_exempt"`
	TotalSpent          *decimal.Decimal `json:"total_spent"`
	Phone               string           `json:"phone"`
	Tags                string           `json:"tags"`
	LastOrderId         int              `json:"last_order_id"`
	AcceptsMarketing    bool             `json:"accepts_marketing"`
	CreatedAt           *time.Time       `json:"created_at"`
	UpdatedAt           *time.Time       `json:"updated_at"`
}

// Represents the result from the customers/X.json endpoint
type CustomerResource struct {
	Customer *Customer `json:"customer"`
}

// Represents the result from the customers.json endpoint
type CustomersResource struct {
	Customers []Customer `json:"customers"`
}

// Represents the options available when searching for a customer
type CustomerSearchOptions struct {
	Page   int    `url:"page,omitempty"`
	Limit  int    `url:"limit,omitempty"`
	Fields string `url:"fields,omitempty"`
	Order  string `url:"order,omitempty"`
	Query  string `url:"query,omitempty"`
}

// List customers
func (s *CustomerServiceOp) List(options interface{}) ([]Customer, error) {
	path := fmt.Sprintf("%s.json", customersBasePath)
	resource := new(CustomersResource)
	err := s.client.Get(path, resource, options)
	return resource.Customers, err
}

// Count customers
func (s *CustomerServiceOp) Count(options interface{}) (int, error) {
	path := fmt.Sprintf("%s/count.json", customersBasePath)
	return s.client.Count(path, options)
}

// Get customer
func (s *CustomerServiceOp) Get(customerID int, options interface{}) (*Customer, error) {
	path := fmt.Sprintf("%s/%v.json", customersBasePath, customerID)
	resource := new(CustomerResource)
	err := s.client.Get(path, resource, options)
	return resource.Customer, err
}

// Create a new customer
func (s *CustomerServiceOp) Create(customer Customer) (*Customer, error) {
	path := fmt.Sprintf("%s.json", customersBasePath)
	wrappedData := CustomerResource{Customer: &customer}
	resource := new(CustomerResource)
	err := s.client.Post(path, wrappedData, resource)
	return resource.Customer, err
}

// Update an existing customer
func (s *CustomerServiceOp) Update(customer Customer) (*Customer, error) {
	path := fmt.Sprintf("%s/%d.json", customersBasePath, customer.ID)
	wrappedData := CustomerResource{Customer: &customer}
	resource := new(CustomerResource)
	err := s.client.Put(path, wrappedData, resource)
	return resource.Customer, err
}

// Search customers
func (s *CustomerServiceOp) Search(options interface{}) ([]Customer, error) {
	path := fmt.Sprintf("%s/search.json", customersBasePath)
	resource := new(CustomersResource)
	err := s.client.Get(path, resource, options)
	return resource.Customers, err
}
<commit_msg>Add missing customer detail (#81)<commit_after>package goshopify

import (
	"fmt"
	"time"

	"github.com/shopspring/decimal"
)

const customersBasePath = "admin/customers"

// CustomerService is an interface for interfacing with the customers endpoints
// of the Shopify API.
// See: https://help.shopify.com/api/reference/customer
type CustomerService interface {
	List(interface{}) ([]Customer, error)
	Count(interface{}) (int, error)
	Get(int, interface{}) (*Customer, error)
	Search(interface{}) ([]Customer, error)
	Create(Customer) (*Customer, error)
	Update(Customer) (*Customer, error)
}

// CustomerServiceOp handles communication with the product related methods of
// the Shopify API.
type CustomerServiceOp struct {
	client *Client
}

// Customer represents a Shopify customer
type Customer struct {
	ID                  int              `json:"id"`
	Email               string           `json:"email"`
	FirstName           string           `json:"first_name"`
	LastName            string           `json:"last_name"`
	State               string           `json:"state"`
	Note                string           `json:"note"`
	VerifiedEmail       bool             `json:"verified_email"`
	MultipassIdentifier string           `json:"multipass_identifier"`
	OrdersCount         int              `json:"orders_count"`
	TaxExempt           bool             `json:"tax_exempt"`
	TotalSpent          *decimal.Decimal `json:"total_spent"`
	Phone               string           `json:"phone"`
	Tags                string           `json:"tags"`
	LastOrderId         int              `json:"last_order_id"`
	LastOrderName       string           `json:"last_order_name"`
	AcceptsMarketing    bool             `json:"accepts_marketing"`
	CreatedAt           *time.Time       `json:"created_at"`
	UpdatedAt           *time.Time       `json:"updated_at"`
}

// Represents the result from the customers/X.json endpoint
type CustomerResource struct {
	Customer *Customer `json:"customer"`
}

// Represents the result from the customers.json endpoint
type CustomersResource struct {
	Customers []Customer `json:"customers"`
}

// Represents the options available when searching for a customer
type CustomerSearchOptions struct {
	Page   int    `url:"page,omitempty"`
	Limit  int    `url:"limit,omitempty"`
	Fields string `url:"fields,omitempty"`
	Order  string `url:"order,omitempty"`
	Query  string `url:"query,omitempty"`
}

// List customers
func (s *CustomerServiceOp) List(options interface{}) ([]Customer, error) {
	path := fmt.Sprintf("%s.json", customersBasePath)
	resource := new(CustomersResource)
	err := s.client.Get(path, resource, options)
	return resource.Customers, err
}

// Count customers
func (s *CustomerServiceOp) Count(options interface{}) (int, error) {
	path := fmt.Sprintf("%s/count.json", customersBasePath)
	return s.client.Count(path, options)
}

// Get customer
func (s *CustomerServiceOp) Get(customerID int, options interface{}) (*Customer, error) {
	path := fmt.Sprintf("%s/%v.json", customersBasePath, customerID)
	resource := new(CustomerResource)
	err := s.client.Get(path, resource, options)
	return resource.Customer, err
}

// Create a new customer
func (s *CustomerServiceOp) Create(customer Customer) (*Customer, error) {
	path := fmt.Sprintf("%s.json", customersBasePath)
	wrappedData := CustomerResource{Customer: &customer}
	resource := new(CustomerResource)
	err := s.client.Post(path, wrappedData, resource)
	return resource.Customer, err
}

// Update an existing customer
func (s *CustomerServiceOp) Update(customer Customer) (*Customer, error) {
	path := fmt.Sprintf("%s/%d.json", customersBasePath, customer.ID)
	wrappedData := CustomerResource{Customer: &customer}
	resource := new(CustomerResource)
	err := s.client.Put(path, wrappedData, resource)
	return resource.Customer, err
}

// Search customers
func (s *CustomerServiceOp) Search(options interface{}) ([]Customer, error) {
	path := fmt.Sprintf("%s/search.json", customersBasePath)
	resource := new(CustomersResource)
	err := s.client.Get(path, resource, options)
	return resource.Customers, err
}
<|endoftext|>