<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/jmcvetta/neoism"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"time"
)

func main() {

	neoUrl := os.Getenv("NEO_URL")
	if neoUrl == "" {
		log.Println("no $NEO_URL set, defaulting to local")
		neoUrl = "http://localhost:7474/db/data"
	}
	log.Printf("connecting to %s\n", neoUrl)

	var err error
	db, err = neoism.Connect(neoUrl)
	if err != nil {
		panic(err)
	}

	ensureIndexes(db)

	writeQueue = make(chan organisation, 2048)

	port := 8080

	m := mux.NewRouter()
	http.Handle("/", m)

	m.HandleFunc("/organisations/{uuid}", idWriteHandler).Methods("PUT")
	m.HandleFunc("/organisations/", allWriteHandler).Methods("PUT")

	go func() {
		log.Printf("listening on %d", port)
		if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
			log.Printf("web stuff failed: %v\n", err)
		}
	}()

	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		orgWriteLoop()
		wg.Done()
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// wait for ctrl-c
	<-c
	close(writeQueue)
	wg.Wait()
	println("exiting")

}

func ensureIndexes(db *neoism.Database) {
	ensureIndex(db, "Organisation", "uuid")
	ensureIndex(db, "Concept", "uuid")
}

func ensureIndex(db *neoism.Database, label string, prop string) {
	indexes, err := db.Indexes(label)
	if err != nil {
		panic(err)
	}
	for _, ind := range indexes {
		if len(ind.PropertyKeys) == 1 && ind.PropertyKeys[0] == prop {
			return
		}
	}
	if _, err := db.CreateIndex(label, prop); err != nil {
		panic(err)
	}
}

var db *neoism.Database

var writeQueue chan organisation

func orgWriteLoop() {
	var qs []*neoism.CypherQuery

	timer := time.NewTimer(1 * time.Second)

	defer println("write loop exited")
	for {
		select {
		case o, ok := <-writeQueue:
			if !ok {
				return
			}
			for _, q := range toQueries(o) {
				qs = append(qs, q)
			}
			if len(qs) < 1024 {
				timer.Reset(1 * time.Second)
				continue
			}
		case <-timer.C:
		}
		if len(qs) > 0 {
			fmt.Printf("writing batch of %d\n", len(qs))
			err := db.CypherBatch(qs)
			if err != nil {
				panic(err)
			}
			fmt.Printf("wrote batch of %d\n", len(qs))
			qs = qs[0:0]
			timer.Stop()
		}
	}
}

func allWriteHandler(w http.ResponseWriter, r *http.Request) {

	dec := json.NewDecoder(r.Body)

	for {
		var o organisation
		err := dec.Decode(&o)
		if err == io.ErrUnexpectedEOF {
			println("eof")
			return
		}
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		writeQueue <- o
	}

	w.WriteHeader(http.StatusAccepted)
}

func idWriteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	UUID := vars["uuid"]

	var o organisation
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&o)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if o.UUID != UUID {
		fmt.Printf("%v\n", o)
		http.Error(w, fmt.Sprintf("id does not match: %v %v", o.UUID, UUID), http.StatusBadRequest)
		return
	}

	writeQueue <- o

	w.WriteHeader(http.StatusAccepted)
}

func toQueries(o organisation) []*neoism.CypherQuery {
	props := toProps(o)

	var queries []*neoism.CypherQuery

	queries = append(queries, &neoism.CypherQuery{
		Statement: `
			MERGE (n:Organisation {uuid: {uuid}})
			SET n = {allProps}
			RETURN n
		`,
		Parameters: map[string]interface{}{
			"uuid":     o.UUID,
			"allProps": props,
		},
	})

	t := string(o.Type)
	if t != "Organisation" && t != "" {
		queries = append(queries, &neoism.CypherQuery{
			Statement: fmt.Sprintf("MERGE (n:Organisation {uuid: {uuid}}) SET n :%s", t),
			Parameters: map[string]interface{}{
				"uuid": o.UUID,
			},
		})
	}

	if o.ParentOrganisation != "" {
		queries = append(queries, &neoism.CypherQuery{
			Statement: `
			MERGE (n:Organisation {uuid: {uuid}})
			MERGE (p:Organisation {uuid: {puuid}})
			MERGE (n)-[r:SUB_ORG_OF]->(p)
		`,
			Parameters: map[string]interface{}{
				"uuid":  o.UUID,
				"puuid": o.ParentOrganisation,
			},
		})
	}

	if o.IndustryClassification != "" {
		queries = append(queries, &neoism.CypherQuery{
			Statement: `
			MERGE (n:Organisation {uuid: {uuid}})
			MERGE (ic:Industry {uuid: {icuuid}})
			MERGE (n)-[r:IN_INDUSTRY]->(ic)
		`,
			Parameters: map[string]interface{}{
				"uuid":   o.UUID,
				"icuuid": o.IndustryClassification,
			},
		})

	}

	return queries
}

func toProps(o organisation) neoism.Props {
	p := map[string]interface{}{
		"uuid": o.UUID,
	}

	if o.Extinct == true {
		p["extinct"] = true
	}
	if o.FormerNames != nil && len(o.FormerNames) != 0 {
		p["formerNames"] = o.FormerNames
	}
	if o.HiddenLabel != "" {
		p["hiddenLabel"] = o.HiddenLabel
	}
	if o.LegalName != "" {
		p["legalName"] = o.LegalName
	}
	if o.LocalNames != nil && len(o.LocalNames) != 0 {
		p["localNames"] = o.LocalNames
	}
	if o.ProperName != "" {
		p["properName"] = o.ProperName
	}
	if o.ShortName != "" {
		p["shortName"] = o.ShortName
	}
	if o.TradeNames != nil && len(o.TradeNames) != 0 {
		p["tradeNames"] = o.TradeNames
	}
	for _, identifier := range o.Identifiers {
		if identifier.Authority == fsAuthority {
			p["factsetIdentifier"] = identifier.IdentifierValue
		}
	}
	p["uuid"] = o.UUID

	return neoism.Props(p)
}

const (
	fsAuthority = "http://api.ft.com/system/FACTSET-EDM"
)
<commit_msg>match via concept, set org label<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/jmcvetta/neoism"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"time"
)

func main() {

	neoUrl := os.Getenv("NEO_URL")
	if neoUrl == "" {
		log.Println("no $NEO_URL set, defaulting to local")
		neoUrl = "http://localhost:7474/db/data"
	}
	log.Printf("connecting to %s\n", neoUrl)

	var err error
	db, err = neoism.Connect(neoUrl)
	if err != nil {
		panic(err)
	}

	ensureIndexes(db)

	writeQueue = make(chan organisation, 2048)

	port := 8080

	m := mux.NewRouter()
	http.Handle("/", m)

	m.HandleFunc("/organisations/{uuid}", idWriteHandler).Methods("PUT")
	m.HandleFunc("/organisations/", allWriteHandler).Methods("PUT")

	go func() {
		log.Printf("listening on %d", port)
		if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
			log.Printf("web stuff failed: %v\n", err)
		}
	}()

	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		orgWriteLoop()
		wg.Done()
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// wait for ctrl-c
	<-c
	close(writeQueue)
	wg.Wait()
	println("exiting")

}

func ensureIndexes(db *neoism.Database) {
	ensureIndex(db, "Organisation", "uuid")
	ensureIndex(db, "Concept", "uuid")
}

func ensureIndex(db *neoism.Database, label string, prop string) {
	indexes, err := db.Indexes(label)
	if err != nil {
		panic(err)
	}
	for _, ind := range indexes {
		if len(ind.PropertyKeys) == 1 && ind.PropertyKeys[0] == prop {
			return
		}
	}
	if _, err := db.CreateIndex(label, prop); err != nil {
		panic(err)
	}
}

var db *neoism.Database

var writeQueue chan organisation

func orgWriteLoop() {
	var qs []*neoism.CypherQuery

	timer := time.NewTimer(1 * time.Second)

	defer println("write loop exited")
	for {
		select {
		case o, ok := <-writeQueue:
			if !ok {
				return
			}
			for _, q := range toQueries(o) {
				qs = append(qs, q)
			}
			if len(qs) < 1024 {
				timer.Reset(1 * time.Second)
				continue
			}
		case <-timer.C:
		}
		if len(qs) > 0 {
			fmt.Printf("writing batch of %d\n", len(qs))
			err := db.CypherBatch(qs)
			if err != nil {
				panic(err)
			}
			fmt.Printf("wrote batch of %d\n", len(qs))
			qs = qs[0:0]
			timer.Stop()
		}
	}
}

func allWriteHandler(w http.ResponseWriter, r *http.Request) {

	dec := json.NewDecoder(r.Body)

	for {
		var o organisation
		err := dec.Decode(&o)
		if err == io.ErrUnexpectedEOF {
			println("eof")
			return
		}
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		writeQueue <- o
	}

	w.WriteHeader(http.StatusAccepted)
}

func idWriteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	UUID := vars["uuid"]

	var o organisation
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&o)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if o.UUID != UUID {
		fmt.Printf("%v\n", o)
		http.Error(w, fmt.Sprintf("id does not match: %v %v", o.UUID, UUID), http.StatusBadRequest)
		return
	}

	writeQueue <- o

	w.WriteHeader(http.StatusAccepted)
}

func toQueries(o organisation) []*neoism.CypherQuery {
	props := toProps(o)

	var queries []*neoism.CypherQuery

	queries = append(queries, &neoism.CypherQuery{
		Statement: `
			MERGE (n:Concept {uuid: {uuid}})
			SET n = {allProps}
			SET n :Organisation
			RETURN n
		`,
		Parameters: map[string]interface{}{
			"uuid":     o.UUID,
			"allProps": props,
		},
	})

	t := string(o.Type)
	if t != "Organisation" && t != "" {
		queries = append(queries, &neoism.CypherQuery{
			Statement: fmt.Sprintf("MERGE (n:Organisation {uuid: {uuid}}) SET n :%s", t),
			Parameters: map[string]interface{}{
				"uuid": o.UUID,
			},
		})
	}

	if o.ParentOrganisation != "" {
		queries = append(queries, &neoism.CypherQuery{
			Statement: `
			MERGE (n:Organisation {uuid: {uuid}})
			MERGE (p:Organisation {uuid: {puuid}})
			MERGE (n)-[r:SUB_ORG_OF]->(p)
		`,
			Parameters: map[string]interface{}{
				"uuid":  o.UUID,
				"puuid": o.ParentOrganisation,
			},
		})
	}

	if o.IndustryClassification != "" {
		queries = append(queries, &neoism.CypherQuery{
			Statement: `
			MERGE (n:Organisation {uuid: {uuid}})
			MERGE (ic:Industry {uuid: {icuuid}})
			MERGE (n)-[r:IN_INDUSTRY]->(ic)
		`,
			Parameters: map[string]interface{}{
				"uuid":   o.UUID,
				"icuuid": o.IndustryClassification,
			},
		})

	}

	return queries
}

func toProps(o organisation) neoism.Props {
	p := map[string]interface{}{
		"uuid": o.UUID,
	}

	if o.Extinct == true {
		p["extinct"] = true
	}
	if o.FormerNames != nil && len(o.FormerNames) != 0 {
		p["formerNames"] = o.FormerNames
	}
	if o.HiddenLabel != "" {
		p["hiddenLabel"] = o.HiddenLabel
	}
	if o.LegalName != "" {
		p["legalName"] = o.LegalName
	}
	if o.LocalNames != nil && len(o.LocalNames) != 0 {
		p["localNames"] = o.LocalNames
	}
	if o.ProperName != "" {
		p["properName"] = o.ProperName
	}
	if o.ShortName != "" {
		p["shortName"] = o.ShortName
	}
	if o.TradeNames != nil && len(o.TradeNames) != 0 {
		p["tradeNames"] = o.TradeNames
	}
	for _, identifier := range o.Identifiers {
		if identifier.Authority == fsAuthority {
			p["factsetIdentifier"] = identifier.IdentifierValue
		}
	}
	p["uuid"] = o.UUID

	return neoism.Props(p)
}

const (
	fsAuthority = "http://api.ft.com/system/FACTSET-EDM"
)
<|endoftext|>


<commit_before>package ionic

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/ion-channel/ionic/organizations"
	"github.com/ion-channel/ionic/pagination"
	"github.com/ion-channel/ionic/requests"
)

const (
	// OrganizationsCreateEndpoint is the endpoint for creating an organization
	OrganizationsCreateEndpoint = "v1/organizations/createOrganization"
	// OrganizationsGetOwnEndpoint is the endpoint for getting the organizations the user belongs to
	OrganizationsGetOwnEndpoint = "v1/organizations/getOwnOrganizations"
	// OrganizationsGetEndpoint is the endpoint for getting an organization
	OrganizationsGetEndpoint = "v1/organizations/getOrganization"
	// OrganizationsGetBulkEndpoint is the endpoint for getting organizations
	OrganizationsGetBulkEndpoint = "v1/organizations/getOrganizations"
	// OrganizationsUpdateEndpoint is the endpoint for updating an organization
	OrganizationsUpdateEndpoint = "v1/organizations/updateOrganization"
	// OrganizationsDisableEndpoint is the endpoint for disabling an organization
	OrganizationsDisableEndpoint = "v1/organizations/disableOrganization"
	// OrganizationsAddMemberEndpoint is the endpoint for adding an existing user as a member of an organization
	OrganizationsAddMemberEndpoint = "v1/organizations/addMember"
	// OrganizationsUpdateMembersEndpoint is the endpoint for altering existing members of an organization
	OrganizationsUpdateMembersEndpoint = "v1/organizations/updateMembers"
)

// CreateOrganizationOptions represents all the values that can be provided for an organization
// at the time of creation
type CreateOrganizationOptions struct {
	Name string `json:"name"`
}

// CreateOrganization takes a create team options, validates the minimum info is
// present, and makes the calls to create the team. It returns the ID of the created organization
// and any errors it encounters with the API.
func (ic *IonClient) CreateOrganization(opts CreateOrganizationOptions, token string) (*organizations.Organization, error) {
	if opts.Name == "" {
		return nil, fmt.Errorf("name missing from options")
	}

	b, err := json.Marshal(opts)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	b, err = ic.Post(OrganizationsCreateEndpoint, token, nil, *buff, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create organization: %v", err.Error())
	}

	var org organizations.Organization
	err = json.Unmarshal(b, &org)
	if err != nil {
		return nil, fmt.Errorf("failed to parse organization from response: %v", err.Error())
	}

	return &org, nil
}

// GetOwnOrganizations takes a token and returns a list of organizations the user belongs to.
func (ic *IonClient) GetOwnOrganizations(token string) (*[]organizations.UserOrganizationRole, error) {
	resp, _, err := ic.Get(OrganizationsGetOwnEndpoint, token, nil, nil, pagination.Pagination{})
	if err != nil {
		return nil, fmt.Errorf("failed to get own organizations: %v", err.Error())
	}

	var orgs []organizations.UserOrganizationRole
	err = json.Unmarshal(resp, &orgs)
	if err != nil {
		return nil, fmt.Errorf("cannot parse own organizations: %v", err.Error())
	}

	return &orgs, nil
}

// GetOrganization takes an organization id and returns the Ion Channel representation of that organization.
func (ic *IonClient) GetOrganization(id, token string) (*organizations.Organization, error) {
	b, _, err := ic.Get(fmt.Sprintf("%s/%s", OrganizationsGetEndpoint, id), token, nil, nil, pagination.Pagination{})
	if err != nil {
		return nil, fmt.Errorf("failed to get organization: %v", err.Error())
	}

	var organization organizations.Organization
	err = json.Unmarshal(b, &organization)
	if err != nil {
		return nil, fmt.Errorf("cannot parse organization: %v", err.Error())
	}

	return &organization, nil
}

// GetOrganizations takes one or more IDs and returns those organizations.
func (ic *IonClient) GetOrganizations(ids requests.ByIDs, token string) (*[]organizations.Organization, error) {
	b, err := json.Marshal(ids)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	resp, err := ic.Post(OrganizationsGetBulkEndpoint, token, nil, *buff, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get organizations: %v", err.Error())
	}

	var orgs []organizations.Organization
	err = json.Unmarshal(resp, &orgs)
	if err != nil {
		return nil, fmt.Errorf("cannot parse organizations: %v", err.Error())
	}

	return &orgs, nil
}

// UpdateOrganization takes an organization ID, and the fields to update, returns the updated organization.
func (ic *IonClient) UpdateOrganization(id string, name string, token string) (*organizations.Organization, error) {
	req := struct {
		Name string `json:"name"`
	}{Name: name}

	b, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	resp, err := ic.Put(fmt.Sprintf("%s/%s", OrganizationsUpdateEndpoint, id), token, nil, *buff, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to update organization: %v", err.Error())
	}

	var org organizations.Organization
	err = json.Unmarshal(resp, &org)
	if err != nil {
		return nil, fmt.Errorf("cannot parse organization: %v", err.Error())
	}

	return &org, nil
}

// DisableOrganization takes an organization ID and returns any errors that occurred.
func (ic *IonClient) DisableOrganization(id string, token string) error {
	_, err := ic.Delete(fmt.Sprintf("%s/%s", OrganizationsDisableEndpoint, id), token, nil, nil)
	if err != nil {
		return fmt.Errorf("failed to disable organization: %v", err.Error())
	}

	return nil
}

// AddMemberToOrganization takes an organization ID, a user ID, and a role, and returns any errors that occurred.
func (ic *IonClient) AddMemberToOrganization(organizationID string, userID string, role organizations.OrganizationRole, token string) error {
	req := struct {
		UserID string                         `json:"user_id"`
		Role   organizations.OrganizationRole `json:"role"`
	}{
		UserID: userID,
		Role:   role,
	}

	b, err := json.Marshal(req)
	if err != nil {
		return fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	_, err = ic.Post(fmt.Sprintf("%s/%s", OrganizationsAddMemberEndpoint, organizationID), token, nil, *buff, nil)
	if err != nil {
		return fmt.Errorf("failed to add member to organization: %v", err.Error())
	}

	return nil
}

// UpdateOrganizationMembers takes an organization ID and a slice of UpdateOrganizationMemberInput, and returns any errors that occurred.
func (ic *IonClient) UpdateOrganizationMembers(organizationID string, usersToUpdate []organizations.OrganizationMemberUpdate, token string) error {
	b, err := json.Marshal(usersToUpdate)
	if err != nil {
		return fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	_, err = ic.Put(fmt.Sprintf("%s/%s", OrganizationsUpdateMembersEndpoint, organizationID), token, nil, *buff, nil)
	if err != nil {
		return fmt.Errorf("failed to update organization members: %v", err.Error())
	}

	return nil
}
<commit_msg>Correct expected arguments<commit_after>package ionic

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/ion-channel/ionic/organizations"
	"github.com/ion-channel/ionic/pagination"
	"github.com/ion-channel/ionic/requests"
)

const (
	// OrganizationsCreateEndpoint is the endpoint for creating an organization
	OrganizationsCreateEndpoint = "v1/organizations/createOrganization"
	// OrganizationsGetOwnEndpoint is the endpoint for getting the organizations the user belongs to
	OrganizationsGetOwnEndpoint = "v1/organizations/getOwnOrganizations"
	// OrganizationsGetEndpoint is the endpoint for getting an organization
	OrganizationsGetEndpoint = "v1/organizations/getOrganization"
	// OrganizationsGetBulkEndpoint is the endpoint for getting organizations
	OrganizationsGetBulkEndpoint = "v1/organizations/getOrganizations"
	// OrganizationsUpdateEndpoint is the endpoint for updating an organization
	OrganizationsUpdateEndpoint = "v1/organizations/updateOrganization"
	// OrganizationsDisableEndpoint is the endpoint for disabling an organization
	OrganizationsDisableEndpoint = "v1/organizations/disableOrganization"
	// OrganizationsAddMemberEndpoint is the endpoint for adding an existing user as a member of an organization
	OrganizationsAddMemberEndpoint = "v1/organizations/addMember"
	// OrganizationsUpdateMembersEndpoint is the endpoint for altering existing members of an organization
	OrganizationsUpdateMembersEndpoint = "v1/organizations/updateMembers"
)

// CreateOrganizationOptions represents all the values that can be provided for an organization
// at the time of creation
type CreateOrganizationOptions struct {
	Name string `json:"name"`
}

// CreateOrganization takes a create team options, validates the minimum info is
// present, and makes the calls to create the team. It returns the ID of the created organization
// and any errors it encounters with the API.
func (ic *IonClient) CreateOrganization(opts CreateOrganizationOptions, token string) (*organizations.Organization, error) {
	if opts.Name == "" {
		return nil, fmt.Errorf("name missing from options")
	}

	b, err := json.Marshal(opts)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	b, err = ic.Post(OrganizationsCreateEndpoint, token, nil, *buff, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create organization: %v", err.Error())
	}

	var org organizations.Organization
	err = json.Unmarshal(b, &org)
	if err != nil {
		return nil, fmt.Errorf("failed to parse organization from response: %v", err.Error())
	}

	return &org, nil
}

// GetOwnOrganizations takes a token and returns a list of organizations the user belongs to.
func (ic *IonClient) GetOwnOrganizations(token string) (*[]organizations.UserOrganizationRole, error) {
	resp, _, err := ic.Get(OrganizationsGetOwnEndpoint, token, nil, nil, pagination.Pagination{})
	if err != nil {
		return nil, fmt.Errorf("failed to get own organizations: %v", err.Error())
	}

	var orgs []organizations.UserOrganizationRole
	err = json.Unmarshal(resp, &orgs)
	if err != nil {
		return nil, fmt.Errorf("cannot parse own organizations: %v", err.Error())
	}

	return &orgs, nil
}

// GetOrganization takes an organization id and returns the Ion Channel representation of that organization.
func (ic *IonClient) GetOrganization(id, token string) (*organizations.Organization, error) {
	b, _, err := ic.Get(fmt.Sprintf("%s/%s", OrganizationsGetEndpoint, id), token, nil, nil, pagination.Pagination{})
	if err != nil {
		return nil, fmt.Errorf("failed to get organization: %v", err.Error())
	}

	var organization organizations.Organization
	err = json.Unmarshal(b, &organization)
	if err != nil {
		return nil, fmt.Errorf("cannot parse organization: %v", err.Error())
	}

	return &organization, nil
}

// GetOrganizations takes one or more IDs and returns those organizations.
func (ic *IonClient) GetOrganizations(ids requests.ByIDs, token string) (*[]organizations.Organization, error) {
	b, err := json.Marshal(ids)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	resp, err := ic.Post(OrganizationsGetBulkEndpoint, token, nil, *buff, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get organizations: %v", err.Error())
	}

	var orgs []organizations.Organization
	err = json.Unmarshal(resp, &orgs)
	if err != nil {
		return nil, fmt.Errorf("cannot parse organizations: %v", err.Error())
	}

	return &orgs, nil
}

// UpdateOrganization takes an organization ID, and the fields to update, returns the updated organization.
func (ic *IonClient) UpdateOrganization(id string, name string, token string) (*organizations.Organization, error) {
	req := struct {
		Name string `json:"name"`
	}{Name: name}

	b, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	resp, err := ic.Put(fmt.Sprintf("%s/%s", OrganizationsUpdateEndpoint, id), token, nil, *buff, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to update organization: %v", err.Error())
	}

	var org organizations.Organization
	err = json.Unmarshal(resp, &org)
	if err != nil {
		return nil, fmt.Errorf("cannot parse organization: %v", err.Error())
	}

	return &org, nil
}

// DisableOrganization takes an organization ID and returns any errors that occurred.
func (ic *IonClient) DisableOrganization(id string, token string) error {
	_, err := ic.Delete(fmt.Sprintf("%s/%s", OrganizationsDisableEndpoint, id), token, nil, nil)
	if err != nil {
		return fmt.Errorf("failed to disable organization: %v", err.Error())
	}

	return nil
}

// AddMemberToOrganization takes an organization ID, a user ID, and a role, and returns any errors that occurred.
func (ic *IonClient) AddMemberToOrganization(organizationID string, userID string, roleID string, token string) error {
	req := struct {
		UserID string `json:"user_id"`
		RoleID string `json:"role"`
	}{
		UserID: userID,
		RoleID: roleID,
	}

	b, err := json.Marshal(req)
	if err != nil {
		return fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	_, err = ic.Post(fmt.Sprintf("%s/%s", OrganizationsAddMemberEndpoint, organizationID), token, nil, *buff, nil)
	if err != nil {
		return fmt.Errorf("failed to add member to organization: %v", err.Error())
	}

	return nil
}

// UpdateOrganizationMembers takes an organization ID and a slice of UpdateOrganizationMemberInput, and returns any errors that occurred.
func (ic *IonClient) UpdateOrganizationMembers(organizationID string, usersToUpdate []organizations.OrganizationMemberUpdate, token string) error {
	b, err := json.Marshal(usersToUpdate)
	if err != nil {
		return fmt.Errorf("failed to marshal request body: %v", err.Error())
	}

	buff := bytes.NewBuffer(b)

	_, err = ic.Put(fmt.Sprintf("%s/%s", OrganizationsUpdateMembersEndpoint, organizationID), token, nil, *buff, nil)
	if err != nil {
		return fmt.Errorf("failed to update organization members: %v", err.Error())
	}

	return nil
}
<|endoftext|>


<commit_before>//
// Copyright 2015 Rakuten Marketing LLC
//
// 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 gol

import (
	"fmt"

	"github.com/stretchr/testify/suite"
	"github.com/stretchr/testify/assert"
	"github.com/mediaFORGE/gol/fields/severity"
)

type MessageTestSuite struct {
	suite.Suite
}

func (s *MessageTestSuite) TestGet() {
	msg := LogMessage{
		"key": "value",
	}

	assert.Equal(s.T(), msg["key"], "value")

	v, err := msg.Get("key")
	assert.Equal(s.T(), "value", v)
	assert.Nil(s.T(), err)

	v, err = msg.Get("unknown")
	assert.Nil(s.T(), v)
	assert.Equal(s.T(), fmt.Errorf("Message does not contain field unknown"), err)
}

func (s *MessageTestSuite) TestGetSetSeverity() {
	msg := LogMessage{
		"key": "value",
	}

	v, err := msg.GetSeverity()
	assert.Equal(s.T(), severity.Type(-1), v)
	assert.Equal(s.T(), fmt.Errorf("Message does not contain field severity"), err)

	lvl := severity.Type(severity.Emergency)
	msg.SetSeverity(lvl)

	v, err = msg.GetSeverity()
	assert.Equal(s.T(), lvl, v)
	assert.Nil(s.T(), err)
}
<commit_msg>added tests for message.go file.<commit_after>//
// Copyright 2015 Rakuten Marketing LLC
//
// 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 gol

import (
	"fmt"

	"github.com/stretchr/testify/suite"
	"github.com/stretchr/testify/assert"
	"github.com/mediaFORGE/gol/fields/severity"
)

type MessageTestSuite struct {
	suite.Suite
}

func (s *MessageTestSuite) TestGet() {
	msg := LogMessage{
		"key": "value",
	}

	assert.Equal(s.T(), msg["key"], "value")

	v, err := msg.Get("key")
	assert.Equal(s.T(), "value", v)
	assert.Nil(s.T(), err)

	v, err = msg.Get("unknown")
	assert.Nil(s.T(), v)
	assert.Equal(s.T(), fmt.Errorf("Message does not contain field unknown"), err)
}

func (s *MessageTestSuite) TestGetSetSeverity() {
	msg := LogMessage{
		"key": "value",
	}

	v, err := msg.GetSeverity()
	assert.Equal(s.T(), severity.Type(-1), v)
	assert.Equal(s.T(), fmt.Errorf("Message does not contain field severity"), err)

	lvl := severity.Type(severity.Emergency)
	msg.SetSeverity(lvl)

	v, err = msg.GetSeverity()
	assert.Equal(s.T(), lvl, v)
	assert.Nil(s.T(), err)
}

func (s *MessageTestSuite) assertSeverityLevel(expected severity.Type, f NewLogMessageFunc) {
	msg := f()
	severity, err := msg.GetSeverity()
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), expected, severity)
}

func (s *MessageTestSuite) TestNewSeverity() {
	cases := map[int]NewLogMessageFunc{
		severity.Emergency: NewEmergency,
		severity.Alert: NewAlert,
		severity.Critical: NewCritical,
		severity.Error: NewError,
		severity.Warning: NewWarning,
		severity.Notice: NewNotice,
		severity.Info: NewInfo,
		severity.Debug: NewDebug,
	}

	for lvl, f := range cases {
		s.assertSeverityLevel(severity.Type(lvl), f)
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"os"
	"regexp"
	"strconv"
	"time"

	. "../interpreter"
)

var timePattern = regexp.MustCompile(
	"^(?:(\\d\\d)?(\\d\\d))?(\\d\\d)(\\d\\d)(?:(\\d\\d)(\\d\\d))(?:\\.(\\d\\d))?$")

func atoiOr(s string, orelse int) int {
	val, err := strconv.Atoi(s)
	if err != nil {
		return orelse
	}
	return val
}

func readTimeStamp(s string) *time.Time {
	m := timePattern.FindStringSubmatch(s)
	if m == nil {
		return nil
	}
	yy, yy_err := strconv.Atoi(m[2])
	if yy_err != nil {
		yy = time.Now().Year() % 100
	}
	cc, cc_err := strconv.Atoi(m[1])
	if cc_err != nil {
		if yy <= 68 {
			cc = 20
		} else {
			cc = 19
		}
	}
	year := yy + cc*100
	month, _ := strconv.Atoi(m[3])
	mday, _ := strconv.Atoi(m[4])
	hour := atoiOr(m[5], 0)
	min := atoiOr(m[6], 0)
	sec := atoiOr(m[7], 0)
	stamp := time.Date(year, time.Month(month), mday, hour, min, sec, 0, time.Local)
	return &stamp
}

func cmd_touch(this *Interpreter) (ErrorLevel, error) {
	errcnt := 0
	stamp := time.Now()
	for i := 1; i < len(this.Args); i++ {
		arg1 := this.Args[i]
		if arg1 == "-t" {
			i++
			if i >= len(this.Args) {
				fmt.Fprintf(this.Stderr, "-t: Too Few Arguments.\n")
				return ErrorLevel(255), nil
			}
			stamp_ := readTimeStamp(this.Args[i])
			if stamp_ == nil {
				fmt.Fprintf(this.Stderr, "-t: %s: Invalid time format.\n",
					this.Args[i])
				return ErrorLevel(255), nil
			}
			stamp = *stamp_
		} else if arg1 == "-r" {
			i++
			if i >= len(this.Args) {
				fmt.Fprintf(this.Stderr, "-r: Too Few Arguments.\n")
				return ErrorLevel(255), nil
			}
			stat, statErr := os.Stat(this.Args[i])
			if statErr != nil {
				fmt.Fprintf(this.Stderr, "-r: %s: %s\n", this.Args[i], statErr)
				return ErrorLevel(255), nil
			}
			stamp = stat.ModTime()
		} else if arg1[0] == '-' {
			fmt.Fprintf(this.Stderr,
				"%s: built-in touch: Not implemented.\n",
				arg1)
		} else {
			fd, err := os.OpenFile(arg1, os.O_APPEND, 0666)
			if err != nil && os.IsNotExist(err) {
				fd, err = os.Create(arg1)
			}
			if err == nil {
				fd.Close()
				os.Chtimes(arg1, stamp, stamp)
			} else {
				fmt.Fprintln(this.Stderr, err.Error())
				errcnt++
			}
		}
	}
	return ErrorLevel(errcnt), nil
}
<commit_msg>For #127 touch: Add TINY datetime-string validation check<commit_after>package commands

import (
	"fmt"
	"os"
	"regexp"
	"strconv"
	"time"

	. "../interpreter"
)

var timePattern = regexp.MustCompile(
	"^(?:(\\d\\d)?(\\d\\d))?(\\d\\d)(\\d\\d)(?:(\\d\\d)(\\d\\d))(?:\\.(\\d\\d))?$")

func atoiOr(s string, orelse int) int {
	val, err := strconv.Atoi(s)
	if err != nil {
		return orelse
	}
	return val
}

func readTimeStamp(s string) *time.Time {
	m := timePattern.FindStringSubmatch(s)
	if m == nil {
		return nil
	}
	yy, yy_err := strconv.Atoi(m[2])
	if yy_err != nil {
		yy = time.Now().Year() % 100
	}
	cc, cc_err := strconv.Atoi(m[1])
	if cc_err != nil {
		if yy <= 68 {
			cc = 20
		} else {
			cc = 19
		}
	} else if cc < 19 {
		return nil
	}
	year := yy + cc*100
	month, _ := strconv.Atoi(m[3])
	if month < 1 || month > 12 {
		return nil
	}
	mday, _ := strconv.Atoi(m[4])
	if mday < 1 || mday > 31 {
		return nil
	}
	hour := atoiOr(m[5], 0)
	if hour < 0 || hour >= 24 {
		return nil
	}
	min := atoiOr(m[6], 0)
	if min < 0 || min >= 60 {
		return nil
	}
	sec := atoiOr(m[7], 0)
	if sec < 0 || sec > 60 {
		return nil
	}
	stamp := time.Date(year, time.Month(month), mday, hour, min, sec, 0, time.Local)
	return &stamp
}

func cmd_touch(this *Interpreter) (ErrorLevel, error) {
	errcnt := 0
	stamp := time.Now()
	for i := 1; i < len(this.Args); i++ {
		arg1 := this.Args[i]
		if arg1 == "-t" {
			i++
			if i >= len(this.Args) {
				fmt.Fprintf(this.Stderr, "-t: Too Few Arguments.\n")
				return ErrorLevel(255), nil
			}
			stamp_ := readTimeStamp(this.Args[i])
			if stamp_ == nil {
				fmt.Fprintf(this.Stderr, "-t: %s: Invalid time format.\n",
					this.Args[i])
				return ErrorLevel(255), nil
			}
			stamp = *stamp_
		} else if arg1 == "-r" {
			i++
			if i >= len(this.Args) {
				fmt.Fprintf(this.Stderr, "-r: Too Few Arguments.\n")
				return ErrorLevel(255), nil
			}
			stat, statErr := os.Stat(this.Args[i])
			if statErr != nil {
				fmt.Fprintf(this.Stderr, "-r: %s: %s\n", this.Args[i], statErr)
				return ErrorLevel(255), nil
			}
			stamp = stat.ModTime()
		} else if arg1[0] == '-' {
			fmt.Fprintf(this.Stderr,
				"%s: built-in touch: Not implemented.\n",
				arg1)
		} else {
			fd, err := os.OpenFile(arg1, os.O_APPEND, 0666)
			if err != nil && os.IsNotExist(err) {
				fd, err = os.Create(arg1)
			}
			if err == nil {
				fd.Close()
				os.Chtimes(arg1, stamp, stamp)
			} else {
				fmt.Fprintln(this.Stderr, err.Error())
				errcnt++
			}
		}
	}
	return ErrorLevel(errcnt), nil
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"
	"testing"

	"github.com/maxence-charriere/go-app/v8/pkg/errors"
	"github.com/stretchr/testify/require"
)

func TestKindString(t *testing.T) {
	utests := []struct {
		kind           Kind
		expectedString string
	}{
		{
			kind:           UndefinedElem,
			expectedString: "undefined",
		},
		{
			kind:           SimpleText,
			expectedString: "text",
		},
		{
			kind:           HTML,
			expectedString: "html",
		},
		{
			kind:           Component,
			expectedString: "component",
		},
		{
			kind:           Selector,
			expectedString: "selector",
		},
	}

	for _, u := range utests {
		t.Run(u.expectedString, func(t *testing.T) {
			require.Equal(t, u.expectedString, u.kind.String())
		})
	}
}

func TestFilterUIElems(t *testing.T) {
	var nilText *text

	simpleText := Text("hello")

	expectedResult := []UI{
		simpleText,
	}

	res := FilterUIElems(nil, nilText, simpleText)
	require.Equal(t, expectedResult, res)
}

func TestIsErrReplace(t *testing.T) {
	utests := []struct {
		scenario     string
		err          error
		isErrReplace bool
	}{
		{
			scenario:     "error is a replace error",
			err:          errors.New("test").Tag("replace", true),
			isErrReplace: true,
		},
		{
			scenario:     "error is not a replace error",
			err:          errors.New("test").Tag("test", true),
			isErrReplace: false,
		},
		{
			scenario:     "standard error is not a replace error",
			err:          fmt.Errorf("test"),
			isErrReplace: false,
		},
		{
			scenario:     "nil error is not a replace error",
			err:          nil,
			isErrReplace: false,
		},
	}

	for _, u := range utests {
		t.Run(u.scenario, func(t *testing.T) {
			res := isErrReplace(u.err)
			require.Equal(t, u.isErrReplace, res)
		})
	}
}

type mountTest struct {
	scenario string
	node     UI
}

func testMountDismount(t *testing.T, utests []mountTest) {
	for _, u := range utests {
		t.Run(u.scenario, func(t *testing.T) {
			n := u.node

			d := NewClientTestingDispatcher(n)

			d.Consume()
			testMounted(t, n)

			d.Close()
			testDismounted(t, n)
		})
	}
}

func testMounted(t *testing.T, n UI) {
	require.NotNil(t, n.JSValue())
	require.NotNil(t, n.Dispatcher())
	require.True(t, n.Mounted())

	switch n.Kind() {
	case HTML, Component:
		require.NoError(t, n.context().Err())
		require.NotNil(t, n.self())
	}

	for _, c := range n.children() {
		require.Equal(t, n, c.parent())
		testMounted(t, c)
	}
}

func testDismounted(t *testing.T, n UI) {
	require.Nil(t, n.JSValue())
	require.Nil(t, n.Dispatcher())
	require.False(t, n.Mounted())

	switch n.Kind() {
	case HTML, Component:
		require.Error(t, n.context().Err())
		require.Nil(t, n.self())
	}

	for _, c := range n.children() {
		testDismounted(t, c)
	}
}

type updateTest struct {
	scenario   string
	a          UI
	b          UI
	matches    []TestUIDescriptor
	replaceErr bool
}

func testUpdate(t *testing.T, utests []updateTest) {
	for _, u := range utests {
		t.Run(u.scenario, func(t *testing.T) {
			d := NewClientTestingDispatcher(u.a)
			defer d.Close()
			d.Consume()

			err := update(u.a, u.b)
			if u.replaceErr {
				require.Error(t, err)
				require.True(t, isErrReplace(err))
				return
			}

			require.NoError(t, err)
			for _, d := range u.matches {
				require.NoError(t, TestMatch(u.a, d))
			}
		})
	}
}

func TestHTMLString(t *testing.T) {
	utests := []struct {
		scenario string
		root     UI
	}{
		{
			scenario: "hmtl element",
			root:     Div().ID("test"),
		},
		{
			scenario: "text",
			root:     Text("hello"),
		},
		{
			scenario: "component",
			root:     &hello{},
		},
		{
			scenario: "nested component",
			root:     Div().Body(&hello{}),
		},
		{
			scenario: "nested nested component",
			root:     Div().Body(&foo{Bar: "bar"}),
		},
	}

	for _, u := range utests {
		t.Run(u.scenario, func(t *testing.T) {
			html := HTMLString(u.root)
			t.Log(html)
		})
	}
}
<commit_msg>Update node_test.go<commit_after>package app

import (
	"fmt"
	"testing"

	"github.com/maxence-charriere/go-app/v8/pkg/errors"
	"github.com/stretchr/testify/require"
)

func TestKindString(t *testing.T) {
	utests := []struct {
		kind           Kind
		expectedString string
	}{
		{
			kind:           UndefinedElem,
			expectedString: "undefined",
		},
		{
			kind:           SimpleText,
			expectedString: "text",
		},
		{
			kind:           HTML,
			expectedString: "html",
		},
		{
			kind:           Component,
			expectedString: "component",
		},
		{
			kind:           Selector,
			expectedString: "selector",
		},
	}

	for _, u := range utests {
		t.Run(u.expectedString, func(t *testing.T) {
			require.Equal(t, u.expectedString, u.kind.String())
		})
	}
}

func TestFilterUIElems(t *testing.T) {
	var nilText *text

	simpleText := Text("hello")

	expectedResult := []UI{
		simpleText,
	}

	res := FilterUIElems(nil, nilText, simpleText)
	require.Equal(t, expectedResult, res)
}

func TestIsErrReplace(t *testing.T) {
	utests := []struct {
		scenario     string
		err          error
		isErrReplace bool
	}{
		{
			scenario:     "error is a replace error",
			err:          errors.New("test").Tag("replace", true),
			isErrReplace: true,
		},
		{
			scenario:     "error is not a replace error",
			err:          errors.New("test").Tag("test", true),
			isErrReplace: false,
		},
		{
			scenario:     "standard error is not a replace error",
			err:          fmt.Errorf("test"),
			isErrReplace: false,
		},
		{
			scenario:     "nil error is not a replace error",
			err:          nil,
			isErrReplace: false,
		},
	}

	for _, u := range utests {
		t.Run(u.scenario, func(t *testing.T) {
			res := isErrReplace(u.err)
			require.Equal(t, u.isErrReplace, res)
		})
	}
}

type mountTest struct {
	scenario string
	node     UI
}

func testMountDismount(t *testing.T, utests []mountTest) {
	for _, u := range utests {
		t.Run(u.scenario, func(t *testing.T) {
			n := u.node

			d := NewClientTestingDispatcher(n)

			d.Consume()
			testMounted(t, n)

			d.Close()
			testDismounted(t, n)
		})
	}
}

func testMounted(t *testing.T, n UI) {
	require.NotNil(t, n.JSValue())
	require.NotNil(t, n.Dispatcher())
	require.True(t, n.Mounted())

	switch n.Kind() {
	case HTML, Component:
		require.NoError(t, n.context().Err())
		require.NotNil(t, n.self())
	}

	for _, c := range n.children() {
		require.Equal(t, n, c.parent())
		testMounted(t, c)
	}
}

func testDismounted(t *testing.T, n UI) {
	require.Nil(t, n.JSValue())
	require.NotNil(t, n.Dispatcher())
	require.False(t, n.Mounted())

	switch n.Kind() {
	case HTML, Component:
		require.Error(t, n.context().Err())
		require.Nil(t, n.self())
	}

	for _, c := range n.children() {
		testDismounted(t, c)
	}
}

type updateTest struct {
	scenario   string
	a          UI
	b          UI
	matches    []TestUIDescriptor
	replaceErr bool
}

func testUpdate(t *testing.T, utests []updateTest) {
	for _, u := range utests {
		t.Run(u.scenario, func(t *testing.T) {
			d := NewClientTestingDispatcher(u.a)
			defer d.Close()
			d.Consume()

			err := update(u.a, u.b)
			if u.replaceErr {
				require.Error(t, err)
				require.True(t, isErrReplace(err))
				return
			}

			require.NoError(t, err)
			for _, d := range u.matches {
				require.NoError(t, TestMatch(u.a, d))
			}
		})
	}
}

func TestHTMLString(t *testing.T) {
	utests := []struct {
		scenario string
		root     UI
	}{
		{
			scenario: "hmtl element",
			root:     Div().ID("test"),
		},
		{
			scenario: "text",
			root:     Text("hello"),
		},
		{
			scenario: "component",
			root:     &hello{},
		},
		{
			scenario: "nested component",
			root:     Div().Body(&hello{}),
		},
		{
			scenario: "nested nested component",
			root:     Div().Body(&foo{Bar: "bar"}),
		},
	}

	for _, u := range utests {
		t.Run(u.scenario, func(t *testing.T) {
			html := HTMLString(u.root)
			t.Log(html)
		})
	}
}
<|endoftext|>


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

package build

import (
	"fmt"
	"os"
	"path"

	"istio.io/release-builder/pkg/model"
	"istio.io/release-builder/pkg/util"
)

// Archive creates the release archive that users will download. This includes the installation templates,
// istioctl, and various tools.
func Archive(manifest model.Manifest) error {
	// First, build all variants of istioctl (linux, osx, windows). gen-charts is required for manifests compiled in to istioctl.
	if err := util.RunMake(manifest, "istio", nil, "gen-charts", "istioctl-all", "istioctl.completion"); err != nil {
		return fmt.Errorf("failed to make istioctl: %v", err)
	}

	// We build archives for each arch. These contain the same thing except arch specific istioctl
	for _, arch := range []string{"linux-amd64", "linux-armv7", "linux-arm64", "osx", "win"} {
		out := path.Join(manifest.Directory, "work", "archive", arch, fmt.Sprintf("istio-%s", manifest.Version))
		if err := os.MkdirAll(out, 0750); err != nil {
			return err
		}

		// Some files we just directly copy into the release archive
		directCopies := []string{
			"LICENSE",
			"README.md",

			// Setup tools. The tools/ folder contains a bunch of extra junk, so just select exactly what we want
			"tools/certs/Makefile",
			"tools/certs/README.md",
			"tools/convert_RbacConfig_to_ClusterRbacConfig.sh",
			"tools/dump_kubernetes.sh",
		}
		for _, file := range directCopies {
			if err := util.CopyFile(path.Join(manifest.RepoDir("istio"), file), path.Join(out, file)); err != nil {
				return err
			}
		}

		// Set up install and samples. We filter down to only some file patterns
		// TODO - clean this up. We probably include files we don't want and exclude files we do want.
		includePatterns := []string{"*.yaml", "*.md", "cleanup.sh", "*.txt", "*.pem", "*.conf", "*.tpl", "*.json", "Makefile"}
		if err := util.CopyDirFiltered(path.Join(manifest.RepoDir("istio"), "samples"), path.Join(out, "samples"), includePatterns); err != nil {
			return err
		}

		cmd := util.VerboseCommand("./operator/scripts/create_release_charts.sh", "-o", path.Join(out, "manifests"))
		cmd.Dir = manifest.RepoDir("istio")
		cmd.Env = util.StandardEnv(manifest)
		if err := cmd.Run(); err != nil {
			return err
		}
		if err := sanitizeTemplate(manifest, path.Join(out, "manifests/profiles/default.yaml")); err != nil {
			return fmt.Errorf("failed to sanitize operator charts")
		}
		if err := util.CopyDir(path.Join(manifest.RepoDir("istio"), "operator", "samples"), path.Join(out, "samples/operator")); err != nil {
			return err
		}

		// Write manifest
		if err := writeManifest(manifest, out); err != nil {
			return fmt.Errorf("failed to write manifest: %v", err)
		}

		// Copy the istioctl binary over
		istioctlBinary := fmt.Sprintf("istioctl-%s", arch)
		istioctlDest := "istioctl"
		if arch == "win" {
			istioctlBinary += ".exe"
			istioctlDest += ".exe"
		}
		if err := util.CopyFile(path.Join(manifest.RepoOutDir("istio"), istioctlBinary), path.Join(out, "bin", istioctlDest)); err != nil {
			return err
		}
		if err := os.Chmod(path.Join(out, "bin", istioctlDest), 0755); err != nil {
			return err
		}

		// Copy the istioctl completions files to the tools directory
		completionFiles := []string{"istioctl.bash", "_istioctl"}
		for _, file := range completionFiles {
			if err := util.CopyFile(path.Join(manifest.RepoOutDir("istio"), file), path.Join(out, "tools", file)); err != nil {
				return err
			}
		}

		if err := createArchive(arch, manifest, out); err != nil {
			return err
		}

		if err := createStandaloneIstioctl(arch, manifest, out); err != nil {
			return err
		}
	}
	return nil
}

func createStandaloneIstioctl(arch string, manifest model.Manifest, out string) error {
	var istioctlArchive string
	// Create a stand alone archive for istioctl
	// Windows should use zip, linux and osx tar
	if arch == "win" {
		istioctlArchive = fmt.Sprintf("istioctl-%s-%s.zip", manifest.Version, arch)
		if err := util.ZipFolder(path.Join(out, "bin", "istioctl.exe"), path.Join(out, "bin", istioctlArchive)); err != nil {
			return fmt.Errorf("failed to zip istioctl: %v", err)
		}
	} else {
		istioctlArchive = fmt.Sprintf("istioctl-%s-%s.tar.gz", manifest.Version, arch)
		icmd := util.VerboseCommand("tar", "-czf", istioctlArchive, "istioctl")
		icmd.Dir = path.Join(out, "bin")
		if err := icmd.Run(); err != nil {
			return fmt.Errorf("failed to tar istioctl: %v", err)
		}
	}
	// Copy files over to the output directory
	archivePath := path.Join(out, "bin", istioctlArchive)
	dest := path.Join(manifest.OutDir(), istioctlArchive)
	if err := util.CopyFile(archivePath, dest); err != nil {
		return fmt.Errorf("failed to package %v release archive: %v", arch, err)
	}

	// Create a SHA of the archive
	if err := util.CreateSha(dest); err != nil {
		return fmt.Errorf("failed to package %v: %v", dest, err)
	}
	return nil
}

func createArchive(arch string, manifest model.Manifest, out string) error {
	var archive string
	// Create the archive from all the above files
	// Windows should use zip, linux and osx tar
	if arch == "win" {
		archive = fmt.Sprintf("istio-%s-%s.zip", manifest.Version, arch)
		if err := util.ZipFolder(path.Join(out, "..", fmt.Sprintf("istio-%s", manifest.Version)), path.Join(out, "..", archive)); err != nil {
			return fmt.Errorf("failed to zip istioctl: %v", err)
		}
	} else {
		archive = fmt.Sprintf("istio-%s-%s.tar.gz", manifest.Version, arch)
		cmd := util.VerboseCommand("tar", "-czf", archive, fmt.Sprintf("istio-%s", manifest.Version))
		cmd.Dir = path.Join(out, "..")
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	// Copy files over to the output directory
	archivePath := path.Join(manifest.WorkDir(), "archive", arch, archive)
	dest := path.Join(manifest.OutDir(), archive)
	if err := util.CopyFile(archivePath, dest); err != nil {
		return fmt.Errorf("failed to package %v release archive: %v", arch, err)
	}
	// Create a SHA of the archive
	if err := util.CreateSha(dest); err != nil {
		return fmt.Errorf("failed to package %v: %v", dest, err)
	}
	return nil
}
<commit_msg>remove convert_RbacConfig_to_ClusterRbacConfig.sh from the archive (#340)<commit_after>// Copyright Istio Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package build

import (
	"fmt"
	"os"
	"path"

	"istio.io/release-builder/pkg/model"
	"istio.io/release-builder/pkg/util"
)

// Archive creates the release archive that users will download. This includes the installation templates,
// istioctl, and various tools.
func Archive(manifest model.Manifest) error {
	// First, build all variants of istioctl (linux, osx, windows). gen-charts is required for manifests compiled in to istioctl.
	if err := util.RunMake(manifest, "istio", nil, "gen-charts", "istioctl-all", "istioctl.completion"); err != nil {
		return fmt.Errorf("failed to make istioctl: %v", err)
	}

	// We build archives for each arch. These contain the same thing except arch specific istioctl
	for _, arch := range []string{"linux-amd64", "linux-armv7", "linux-arm64", "osx", "win"} {
		out := path.Join(manifest.Directory, "work", "archive", arch, fmt.Sprintf("istio-%s", manifest.Version))
		if err := os.MkdirAll(out, 0750); err != nil {
			return err
		}

		// Some files we just directly copy into the release archive
		directCopies := []string{
			"LICENSE",
			"README.md",

			// Setup tools. The tools/ folder contains a bunch of extra junk, so just select exactly what we want
			"tools/certs/Makefile",
			"tools/certs/README.md",
			"tools/dump_kubernetes.sh",
		}
		for _, file := range directCopies {
			if err := util.CopyFile(path.Join(manifest.RepoDir("istio"), file), path.Join(out, file)); err != nil {
				return err
			}
		}

		// Set up install and samples. We filter down to only some file patterns
		// TODO - clean this up. We probably include files we don't want and exclude files we do want.
		includePatterns := []string{"*.yaml", "*.md", "cleanup.sh", "*.txt", "*.pem", "*.conf", "*.tpl", "*.json", "Makefile"}
		if err := util.CopyDirFiltered(path.Join(manifest.RepoDir("istio"), "samples"), path.Join(out, "samples"), includePatterns); err != nil {
			return err
		}

		cmd := util.VerboseCommand("./operator/scripts/create_release_charts.sh", "-o", path.Join(out, "manifests"))
		cmd.Dir = manifest.RepoDir("istio")
		cmd.Env = util.StandardEnv(manifest)
		if err := cmd.Run(); err != nil {
			return err
		}
		if err := sanitizeTemplate(manifest, path.Join(out, "manifests/profiles/default.yaml")); err != nil {
			return fmt.Errorf("failed to sanitize operator charts")
		}
		if err := util.CopyDir(path.Join(manifest.RepoDir("istio"), "operator", "samples"), path.Join(out, "samples/operator")); err != nil {
			return err
		}

		// Write manifest
		if err := writeManifest(manifest, out); err != nil {
			return fmt.Errorf("failed to write manifest: %v", err)
		}

		// Copy the istioctl binary over
		istioctlBinary := fmt.Sprintf("istioctl-%s", arch)
		istioctlDest := "istioctl"
		if arch == "win" {
			istioctlBinary += ".exe"
			istioctlDest += ".exe"
		}
		if err := util.CopyFile(path.Join(manifest.RepoOutDir("istio"), istioctlBinary), path.Join(out, "bin", istioctlDest)); err != nil {
			return err
		}
		if err := os.Chmod(path.Join(out, "bin", istioctlDest), 0755); err != nil {
			return err
		}

		// Copy the istioctl completions files to the tools directory
		completionFiles := []string{"istioctl.bash", "_istioctl"}
		for _, file := range completionFiles {
			if err := util.CopyFile(path.Join(manifest.RepoOutDir("istio"), file), path.Join(out, "tools", file)); err != nil {
				return err
			}
		}

		if err := createArchive(arch, manifest, out); err != nil {
			return err
		}

		if err := createStandaloneIstioctl(arch, manifest, out); err != nil {
			return err
		}
	}
	return nil
}

func createStandaloneIstioctl(arch string, manifest model.Manifest, out string) error {
	var istioctlArchive string
	// Create a stand alone archive for istioctl
	// Windows should use zip, linux and osx tar
	if arch == "win" {
		istioctlArchive = fmt.Sprintf("istioctl-%s-%s.zip", manifest.Version, arch)
		if err := util.ZipFolder(path.Join(out, "bin", "istioctl.exe"), path.Join(out, "bin", istioctlArchive)); err != nil {
			return fmt.Errorf("failed to zip istioctl: %v", err)
		}
	} else {
		istioctlArchive = fmt.Sprintf("istioctl-%s-%s.tar.gz", manifest.Version, arch)
		icmd := util.VerboseCommand("tar", "-czf", istioctlArchive, "istioctl")
		icmd.Dir = path.Join(out, "bin")
		if err := icmd.Run(); err != nil {
			return fmt.Errorf("failed to tar istioctl: %v", err)
		}
	}
	// Copy files over to the output directory
	archivePath := path.Join(out, "bin", istioctlArchive)
	dest := path.Join(manifest.OutDir(), istioctlArchive)
	if err := util.CopyFile(archivePath, dest); err != nil {
		return fmt.Errorf("failed to package %v release archive: %v", arch, err)
	}

	// Create a SHA of the archive
	if err := util.CreateSha(dest); err != nil {
		return fmt.Errorf("failed to package %v: %v", dest, err)
	}
	return nil
}

func createArchive(arch string, manifest model.Manifest, out string) error {
	var archive string
	// Create the archive from all the above files
	// Windows should use zip, linux and osx tar
	if arch == "win" {
		archive = fmt.Sprintf("istio-%s-%s.zip", manifest.Version, arch)
		if err := util.ZipFolder(path.Join(out, "..", fmt.Sprintf("istio-%s", manifest.Version)), path.Join(out, "..", archive)); err != nil {
			return fmt.Errorf("failed to zip istioctl: %v", err)
		}
	} else {
		archive = fmt.Sprintf("istio-%s-%s.tar.gz", manifest.Version, arch)
		cmd := util.VerboseCommand("tar", "-czf", archive, fmt.Sprintf("istio-%s", manifest.Version))
		cmd.Dir = path.Join(out, "..")
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	// Copy files over to the output directory
	archivePath := path.Join(manifest.WorkDir(), "archive", arch, archive)
	dest := path.Join(manifest.OutDir(), archive)
	if err := util.CopyFile(archivePath, dest); err != nil {
		return fmt.Errorf("failed to package %v release archive: %v", arch, err)
	}
	// Create a SHA of the archive
	if err := util.CreateSha(dest); err != nil {
		return fmt.Errorf("failed to package %v: %v", dest, err)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package build

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/sys/targets"
)

type fuchsia struct{}

// syzRoot returns $GOPATH/src/github.com/google/syzkaller.
func syzRoot() (string, error) {
	_, selfPath, _, ok := runtime.Caller(0)
	if !ok {
		return "", errors.New("runtime.Caller failed")
	}

	return filepath.Abs(filepath.Join(filepath.Dir(selfPath), "../.."))
}

func (fu fuchsia) build(params *Params) error {
	syzDir, err := syzRoot()
	if err != nil {
		return err
	}

	sysTarget := targets.Get("fuchsia", params.TargetArch)
	if sysTarget == nil {
		return fmt.Errorf("unsupported fuchsia arch %v", params.TargetArch)
	}
	arch := sysTarget.KernelHeaderArch
	product := fmt.Sprintf("%s.%s", "core", arch)
	if _, err := runSandboxed(time.Hour, params.KernelDir,
		"scripts/fx", "--dir", "out/"+arch,
		"set", product,
		"--args", fmt.Sprintf(`syzkaller_dir="%s"`, syzDir),
		"--with-base", "//bundles:tools",
		"--with-base", "//src/testing/fuzzing/syzkaller",
		"--variant", "kasan",
	); err != nil {
		return err
	}
	if _, err := runSandboxed(time.Hour*2, params.KernelDir, "scripts/fx", "clean-build"); err != nil {
		return err
	}

	// Add ssh keys to the zbi image so syzkaller can access the fuchsia vm.
	_, sshKeyPub, err := genSSHKeys(params.OutputDir)
	if err != nil {
		return err
	}

	sshZBI := filepath.Join(params.OutputDir, "initrd")
	kernelZBI := filepath.Join(params.KernelDir, "out", arch, "fuchsia.zbi")
	authorizedKeys := fmt.Sprintf("data/ssh/authorized_keys=%s", sshKeyPub)

	if _, err := osutil.RunCmd(time.Minute, params.KernelDir, "out/"+arch+"/host_x64/zbi",
		"-o", sshZBI, kernelZBI, "--entry", authorizedKeys); err != nil {
		return err
	}

	for src, dst := range map[string]string{
		"out/" + arch + "/obj/build/images/fvm.blk":                               "image",
		"out/" + arch + ".zircon/kernel-" + arch + "-kasan/obj/kernel/zircon.elf": "obj/zircon.elf",
		"out/" + arch + "/multiboot.bin":                                          "kernel",
	} {
		fullSrc := filepath.Join(params.KernelDir, filepath.FromSlash(src))
		fullDst := filepath.Join(params.OutputDir, filepath.FromSlash(dst))
		if err := osutil.CopyFile(fullSrc, fullDst); err != nil {
			return fmt.Errorf("failed to copy %v: %v", src, err)
		}
	}
	return nil
}

func (fu fuchsia) clean(kernelDir, targetArch string) error {
	// We always do clean build because incremental build is frequently broken.
	// So no need to clean separately.
	return nil
}

func runSandboxed(timeout time.Duration, dir, command string, arg ...string) ([]byte, error) {
	cmd := osutil.Command(command, arg...)
	cmd.Dir = dir
	if err := osutil.Sandbox(cmd, true, false); err != nil {
		return nil, err
	}
	return osutil.Run(timeout, cmd)
}

// genSSHKeys generates a pair of ssh keys inside the given directory, named key and key.pub.
// If both files already exist, this function does nothing.
// The function returns the path to both keys.
func genSSHKeys(dir string) (privKey, pubKey string, err error) {
	privKey = filepath.Join(dir, "key")
	pubKey = filepath.Join(dir, "key.pub")

	os.Remove(privKey)
	os.Remove(pubKey)

	if _, err := osutil.RunCmd(time.Minute*5, dir, "ssh-keygen", "-t", "rsa", "-b", "2048",
		"-N", "", "-C", "syzkaller-ssh", "-f", privKey); err != nil {
		return "", "", err
	}
	return privKey, pubKey, nil
}
<commit_msg>pkg/build/fuchsia: expand fvm image<commit_after>// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package build

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/sys/targets"
)

type fuchsia struct{}

// syzRoot returns $GOPATH/src/github.com/google/syzkaller.
func syzRoot() (string, error) {
	_, selfPath, _, ok := runtime.Caller(0)
	if !ok {
		return "", errors.New("runtime.Caller failed")
	}

	return filepath.Abs(filepath.Join(filepath.Dir(selfPath), "../.."))
}

func (fu fuchsia) build(params *Params) error {
	syzDir, err := syzRoot()
	if err != nil {
		return err
	}

	sysTarget := targets.Get("fuchsia", params.TargetArch)
	if sysTarget == nil {
		return fmt.Errorf("unsupported fuchsia arch %v", params.TargetArch)
	}
	arch := sysTarget.KernelHeaderArch
	product := fmt.Sprintf("%s.%s", "core", arch)
	if _, err := runSandboxed(time.Hour, params.KernelDir,
		"scripts/fx", "--dir", "out/"+arch,
		"set", product,
		"--args", fmt.Sprintf(`syzkaller_dir="%s"`, syzDir),
		"--with-base", "//bundles:tools",
		"--with-base", "//src/testing/fuzzing/syzkaller",
		"--variant", "kasan",
	); err != nil {
		return err
	}
	if _, err := runSandboxed(time.Hour*2, params.KernelDir, "scripts/fx", "clean-build"); err != nil {
		return err
	}

	// Add ssh keys to the zbi image so syzkaller can access the fuchsia vm.
	_, sshKeyPub, err := genSSHKeys(params.OutputDir)
	if err != nil {
		return err
	}

	sshZBI := filepath.Join(params.OutputDir, "initrd")
	kernelZBI := filepath.Join(params.KernelDir, "out", arch, "fuchsia.zbi")
	authorizedKeys := fmt.Sprintf("data/ssh/authorized_keys=%s", sshKeyPub)

	if _, err := osutil.RunCmd(time.Minute, params.KernelDir, "out/"+arch+"/host_x64/zbi",
		"-o", sshZBI, kernelZBI, "--entry", authorizedKeys); err != nil {
		return err
	}

	// Copy and extend the fvm.
	fvmTool := filepath.Join("out", arch, "host_x64", "fvm")
	fvmDst := filepath.Join(params.OutputDir, "image")
	fvmSrc := filepath.Join(params.KernelDir, "out", arch, "obj/build/images/fvm.blk")
	if err := osutil.CopyFile(fvmSrc, fvmDst); err != nil {
		return err
	}
	if _, err := osutil.RunCmd(time.Minute*5, params.KernelDir, fvmTool, fvmDst, "extend", "--length", "3G"); err != nil {
		return err
	}

	for src, dst := range map[string]string{
		"out/" + arch + ".zircon/kernel-" + arch + "-kasan/obj/kernel/zircon.elf": "obj/zircon.elf",
		"out/" + arch + "/multiboot.bin":                                          "kernel",
	} {
		fullSrc := filepath.Join(params.KernelDir, filepath.FromSlash(src))
		fullDst := filepath.Join(params.OutputDir, filepath.FromSlash(dst))
		if err := osutil.CopyFile(fullSrc, fullDst); err != nil {
			return fmt.Errorf("failed to copy %v: %v", src, err)
		}
	}
	return nil
}

func (fu fuchsia) clean(kernelDir, targetArch string) error {
	// We always do clean build because incremental build is frequently broken.
	// So no need to clean separately.
	return nil
}

func runSandboxed(timeout time.Duration, dir, command string, arg ...string) ([]byte, error) {
	cmd := osutil.Command(command, arg...)
	cmd.Dir = dir
	if err := osutil.Sandbox(cmd, true, false); err != nil {
		return nil, err
	}
	return osutil.Run(timeout, cmd)
}

// genSSHKeys generates a pair of ssh keys inside the given directory, named key and key.pub.
// If both files already exist, this function does nothing.
// The function returns the path to both keys.
func genSSHKeys(dir string) (privKey, pubKey string, err error) {
	privKey = filepath.Join(dir, "key")
	pubKey = filepath.Join(dir, "key.pub")

	os.Remove(privKey)
	os.Remove(pubKey)

	if _, err := osutil.RunCmd(time.Minute*5, dir, "ssh-keygen", "-t", "rsa", "-b", "2048",
		"-N", "", "-C", "syzkaller-ssh", "-f", privKey); err != nil {
		return "", "", err
	}
	return privKey, pubKey, nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Scaleway. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.md file.

package cli

import (
	"fmt"
	"math/big"
	"text/tabwriter"
	"time"

	"github.com/scaleway/scaleway-cli/pkg/commands"
	"github.com/scaleway/scaleway-cli/pkg/pricing"
	"github.com/scaleway/scaleway-cli/pkg/utils"
	"github.com/scaleway/scaleway-cli/vendor/github.com/docker/docker/pkg/units"
)

var cmdBilling = &Command{
	Exec:        runBilling,
	UsageLine:   "_billing [OPTIONS]",
	Description: "",
	Hidden:      true,
	Help:        "Get resources billing estimation",
}

func init() {
	cmdBilling.Flag.BoolVar(&billingHelp, []string{"h", "-help"}, false, "Print usage")
	cmdBilling.Flag.BoolVar(&billingNoTrunc, []string{"-no-trunc"}, false, "Don't truncate output")
}

// BillingArgs are flags for the `RunBilling` function
type BillingArgs struct {
	NoTrunc bool
}

// Flags
var billingHelp bool    // -h, --help flag
var billingNoTrunc bool // --no-trunc flag

func runBilling(cmd *Command, rawArgs []string) error {
	if billingHelp {
		return cmd.PrintUsage()
	}
	if len(rawArgs) > 0 {
		return cmd.PrintShortUsage()
	}

	// cli parsing
	args := commands.PsArgs{
		NoTrunc: billingNoTrunc,
	}
	ctx := cmd.GetContext(rawArgs)

	// table
	w := tabwriter.NewWriter(ctx.Stdout, 20, 1, 3, ' ', 0)
	defer w.Flush()
	fmt.Fprintf(w, "ID\tNAME\tSTARTED\tMONTH PRICE\n")

	// servers
	servers, err := cmd.API.GetServers(true, 0)
	if err != nil {
		return err
	}

	totalMonthPrice := new(big.Rat)

	for _, server := range *servers {
		if server.State != "running" {
			continue
		}
		shortID := utils.TruncIf(server.Identifier, 8, !args.NoTrunc)
		shortName := utils.TruncIf(utils.Wordify(server.Name), 25, !args.NoTrunc)
		modificationTime, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", server.ModificationDate)
		modificationAgo := time.Now().UTC().Sub(modificationTime)
		shortModificationDate := units.HumanDuration(modificationAgo)
		usage := pricing.NewUsageByPath("/compute/c1/run")
		usage.SetStartEnd(modificationTime, time.Now().UTC())

		totalMonthPrice = totalMonthPrice.Add(totalMonthPrice, usage.Total())

		fmt.Fprintf(w, "server/%s\t%s\t%s\t%s\n", shortID, shortName, shortModificationDate, usage.TotalString())
	}

	fmt.Fprintf(w, "TOTAL\t\t\t%s\n", pricing.PriceString(totalMonthPrice, "EUR"))

	return nil
}
<commit_msg>Added a warning message when running 'scw _billing'<commit_after>// Copyright (C) 2015 Scaleway. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.md file.

package cli

import (
	"fmt"
	"math/big"
	"text/tabwriter"
	"time"

	"github.com/scaleway/scaleway-cli/pkg/commands"
	"github.com/scaleway/scaleway-cli/pkg/pricing"
	"github.com/scaleway/scaleway-cli/pkg/utils"
	"github.com/scaleway/scaleway-cli/vendor/github.com/Sirupsen/logrus"
	"github.com/scaleway/scaleway-cli/vendor/github.com/docker/docker/pkg/units"
)

var cmdBilling = &Command{
	Exec:        runBilling,
	UsageLine:   "_billing [OPTIONS]",
	Description: "",
	Hidden:      true,
	Help:        "Get resources billing estimation",
}

func init() {
	cmdBilling.Flag.BoolVar(&billingHelp, []string{"h", "-help"}, false, "Print usage")
	cmdBilling.Flag.BoolVar(&billingNoTrunc, []string{"-no-trunc"}, false, "Don't truncate output")
}

// BillingArgs are flags for the `RunBilling` function
type BillingArgs struct {
	NoTrunc bool
}

// Flags
var billingHelp bool    // -h, --help flag
var billingNoTrunc bool // --no-trunc flag

func runBilling(cmd *Command, rawArgs []string) error {
	if billingHelp {
		return cmd.PrintUsage()
	}
	if len(rawArgs) > 0 {
		return cmd.PrintShortUsage()
	}

	// cli parsing
	args := commands.PsArgs{
		NoTrunc: billingNoTrunc,
	}
	ctx := cmd.GetContext(rawArgs)

	logrus.Warn("")
	logrus.Warn("Warning: 'scw _billing' is a work-in-progress price estimation tool")
	logrus.Warn("For real usage, visit https://cloud.scaleway.com/#/billing")
	logrus.Warn("")

	// table
	w := tabwriter.NewWriter(ctx.Stdout, 20, 1, 3, ' ', 0)
	defer w.Flush()
	fmt.Fprintf(w, "ID\tNAME\tSTARTED\tMONTH PRICE\n")

	// servers
	servers, err := cmd.API.GetServers(true, 0)
	if err != nil {
		return err
	}

	totalMonthPrice := new(big.Rat)

	for _, server := range *servers {
		if server.State != "running" {
			continue
		}
		shortID := utils.TruncIf(server.Identifier, 8, !args.NoTrunc)
		shortName := utils.TruncIf(utils.Wordify(server.Name), 25, !args.NoTrunc)
		modificationTime, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", server.ModificationDate)
		modificationAgo := time.Now().UTC().Sub(modificationTime)
		shortModificationDate := units.HumanDuration(modificationAgo)
		usage := pricing.NewUsageByPath("/compute/c1/run")
		usage.SetStartEnd(modificationTime, time.Now().UTC())

		totalMonthPrice = totalMonthPrice.Add(totalMonthPrice, usage.Total())

		fmt.Fprintf(w, "server/%s\t%s\t%s\t%s\n", shortID, shortName, shortModificationDate, usage.TotalString())
	}

	fmt.Fprintf(w, "TOTAL\t\t\t%s\n", pricing.PriceString(totalMonthPrice, "EUR"))

	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"

	"github.com/Masterminds/sprig"
	"gopkg.in/flant/yaml.v2"

	"github.com/flant/dapp/pkg/util"
)

func ParseDimgs(dappfilePath string) ([]*Dimg, error) {
	dappfileRenderContent, err := parseDappfileYaml(dappfilePath)
	if err != nil {
		return nil, err
	}

	dappfileRenderPath, err := dumpDappfileRender(dappfilePath, dappfileRenderContent)
	if err != nil {
		return nil, err
	}

	docs, err := splitByDocs(dappfileRenderContent, dappfileRenderPath)
	if err != nil {
		return nil, err
	}

	dimgs, err := splitByDimgs(docs, dappfileRenderContent, dappfileRenderPath)
	if err != nil {
		return nil, err
	}

	return dimgs, nil
}

func dumpDappfileRender(dappfilePath string, dappfileRenderContent string) (string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	dappfileNameParts := strings.Split(path.Base(dappfilePath), ".")
	dappfileRenderNameParts := []string{}
	dappfileRenderNameParts = append(dappfileRenderNameParts, dappfileNameParts[0:len(dappfileNameParts)-1]...)
	dappfileRenderNameParts = append(dappfileRenderNameParts, "render", dappfileNameParts[len(dappfileNameParts)-1])
	dappfileRenderPath := path.Join(wd, fmt.Sprintf(".%s", strings.Join(dappfileRenderNameParts, ".")))

	dappfileRenderFile, err := os.OpenFile(dappfileRenderPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return "", err
	}
	dappfileRenderFile.Write([]byte(dappfileRenderContent))
	dappfileRenderFile.Close()

	return dappfileRenderPath, nil
}

func splitByDocs(dappfileRenderContent string, dappfileRenderPath string) ([]*Doc, error) {
	scanner := bufio.NewScanner(strings.NewReader(dappfileRenderContent))
	scanner.Split(splitYAMLDocument)

	var docs []*Doc
	var line int
	for scanner.Scan() {
		content := make([]byte, len(scanner.Bytes()))
		copy(content, scanner.Bytes())

		if !emptyDocContent(content) {
			docs = append(docs, &Doc{
				Line:           line,
				Content:        content,
				RenderFilePath: dappfileRenderPath,
			})
		}

		contentLines := bytes.Split(content, []byte("\n"))
		if string(contentLines[len(contentLines)-1]) == "" {
			contentLines = contentLines[0 : len(contentLines)-1]
		}
		line += len(contentLines) + 1
	}

	return docs, nil
}

// TODO: переделать на ParseFiles вместо Parse
func parseDappfileYaml(dappfilePath string) (string, error) {
	data, err := ioutil.ReadFile(dappfilePath)
	if err != nil {
		return "", err
	}

	tmpl := template.New("dappfile")
	tmpl.Funcs(funcMap(tmpl))
	if _, err := tmpl.Parse(string(data)); err != nil {
		return "", err
	}
	return executeTemplate(tmpl, "dappfile", map[string]interface{}{"Files": Files{filepath.Dir(dappfilePath)}})
}

func funcMap(tmpl *template.Template) template.FuncMap {
	funcMap := sprig.TxtFuncMap()
	funcMap["include"] = func(name string, data interface{}) (string, error) {
		return executeTemplate(tmpl, name, data)
	}
	return funcMap
}

func executeTemplate(tmpl *template.Template, name string, data interface{}) (string, error) {
	buf := bytes.NewBuffer(nil)
	if err := tmpl.ExecuteTemplate(buf, name, data); err != nil {
		return "", err
	}
	return buf.String(), nil
}

type Files struct {
	HomePath string
}

func (f Files) Get(path string) string {
	b, err := ioutil.ReadFile(filepath.Join(f.HomePath, path))
	if err != nil {
		return ""
	}
	return string(b)
}

func splitYAMLDocument(data []byte, atEOF bool) (advance int, token []byte, err error) {
	const (
		stateLinebegin   = 0
		stateRegularLine = 1
		stateDocDash1    = 2
		stateDocDash2    = 3
		stateDocDash3    = 4
		stateDocSpaces   = 5
		stateDocComment  = 6
	)

	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	state := stateLinebegin
	var index, docLineSize int
	var ch byte
	for index, ch = range data {
		switch ch {
		case '-':
			switch state {
			case stateLinebegin:
				docLineSize = 0
				state = stateDocDash1
			case stateDocDash1:
				docLineSize += 1
				state = stateDocDash2
			case stateDocDash2:
				docLineSize += 1
				state = stateDocDash3
			default:
				state = stateRegularLine
			}
		case '\n':
			switch state {
			case stateDocDash3, stateDocSpaces, stateDocComment:
				advance = index + 1
				token = data[0 : index-docLineSize-1]
				return advance, token, nil
			default:
				state = stateLinebegin
			}
		case ' ', '\r', '\t':
			switch state {
			case stateDocDash3, stateDocSpaces:
				docLineSize += 1
				state = stateDocSpaces
			case stateDocComment:
				docLineSize += 1
			}
		case '#':
			switch state {
			case stateDocDash3, stateDocSpaces, stateDocComment:
				docLineSize += 1
				state = stateDocComment
			default:
				state = stateRegularLine
			}
		default:
			switch state {
			case stateDocComment:
				docLineSize += 1
			default:
				state = stateRegularLine
			}
		}
	}

	switch state {
	case stateDocDash3, stateDocSpaces, stateDocComment:
		return index + 1, data[0 : index-docLineSize], nil
	default:
		return index + 1, data, nil
	}
}

func emptyDocContent(content []byte) bool {
	const (
		stateNone    = 0
		stateComment = 1
	)

	state := stateNone
	for _, ch := range content {
		switch ch {
		case '#':
			state = stateComment
		case '\n':
			if state == stateComment {
				state = stateNone
			}
		case ' ', '\r', '\t':
		default:
			if state != stateComment {
				return false
			}
		}
	}
	return true
}

func splitByDimgs(docs []*Doc, dappfileRenderContent string, dappfileRenderPath string) ([]*Dimg, error) {
	rawDimgs, err := splitByRawDimgs(docs)
	if err != nil {
		return nil, err
	}

	var dimgs []*Dimg
	var artifacts []*DimgArtifact

	for _, rawDimg := range rawDimgs {
		if rawDimg.Type() == "dimgs" {
			if sameDimgs, err := rawDimg.ToDirectives(); err != nil {
				return nil, err
			} else {
				dimgs = append(dimgs, sameDimgs...)
			}
		} else {
			if dimgArtifact, err := rawDimg.ToArtifactDirective(); err != nil {
				return nil, err
			} else {
				artifacts = append(artifacts, dimgArtifact)
			}
		}
	}

	if len(dimgs) == 0 {
		return nil, NewConfigError(fmt.Sprintf("No dimgs defined, at least one dimg required!\n\n%s:\n\n```\n%s```\n", dappfileRenderPath, dappfileRenderContent))
	}

	if err = associateArtifacts(dimgs, artifacts); err != nil {
		return nil, err
	}

	return dimgs, nil
}

func associateArtifacts(dimgs []*Dimg, artifacts []*DimgArtifact) error {
	for _, dimg := range dimgs {
		for _, importArtifact := range dimg.Import {
			if err := importArtifact.AssociateArtifact(artifacts); err != nil {
				return err
			}
		}
	}
	for _, dimg := range artifacts {
		for _, importArtifact := range dimg.Import {
			if err := importArtifact.AssociateArtifact(artifacts); err != nil {
				return err
			}
		}
	}
	return nil
}

func splitByRawDimgs(docs []*Doc) ([]*RawDimg, error) {
	var rawDimgs []*RawDimg
	ParentStack = util.NewStack()
	for _, doc := range docs {
		dimg := &RawDimg{Doc: doc}
		err := yaml.Unmarshal(doc.Content, &dimg)
		if err != nil {
			return nil, newYamlUnmarshalError(err, doc)
		}
		rawDimgs = append(rawDimgs, dimg)
	}

	return rawDimgs, nil
}

func newYamlUnmarshalError(err error, doc *Doc) error {
	switch err.(type) {
	case *ConfigError:
		return err
	default:
		message := err.Error()
		reg, err := regexp.Compile("line ([0-9]+)")
		if err != nil {
			return err
		}

		res := reg.FindStringSubmatch(message)

		if len(res) == 2 {
			line, err := strconv.Atoi(res[1])
			if err != nil {
				return err
			}

			message = reg.ReplaceAllString(message, fmt.Sprintf("line %d", line+doc.Line))
		}
		return NewDetailedConfigError(message, nil, doc)
	}
}
<commit_msg>Config yaml: +++<commit_after>package config

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"

	"github.com/Masterminds/sprig"
	"gopkg.in/flant/yaml.v2"

	"github.com/flant/dapp/pkg/util"
)

func ParseDimgs(dappfilePath string) ([]*Dimg, error) {
	dappfileRenderContent, err := parseDappfileYaml(dappfilePath)
	if err != nil {
		return nil, err
	}

	dappfileRenderPath, err := dumpDappfileRender(dappfilePath, dappfileRenderContent)
	if err != nil {
		return nil, err
	}

	docs, err := splitByDocs(dappfileRenderContent, dappfileRenderPath)
	if err != nil {
		return nil, err
	}

	dimgs, err := splitByDimgs(docs, dappfileRenderContent, dappfileRenderPath)
	if err != nil {
		return nil, err
	}

	return dimgs, nil
}

func dumpDappfileRender(dappfilePath string, dappfileRenderContent string) (string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	dappfileNameParts := strings.Split(path.Base(dappfilePath), ".")
	dappfileRenderNameParts := []string{}
	dappfileRenderNameParts = append(dappfileRenderNameParts, dappfileNameParts[0:len(dappfileNameParts)-1]...)
	dappfileRenderNameParts = append(dappfileRenderNameParts, "render", dappfileNameParts[len(dappfileNameParts)-1])
	dappfileRenderPath := path.Join(wd, fmt.Sprintf(".%s", strings.Join(dappfileRenderNameParts, ".")))

	dappfileRenderFile, err := os.OpenFile(dappfileRenderPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return "", err
	}
	dappfileRenderFile.Write([]byte(dappfileRenderContent))
	dappfileRenderFile.Close()

	return dappfileRenderPath, nil
}

func splitByDocs(dappfileRenderContent string, dappfileRenderPath string) ([]*Doc, error) {
	scanner := bufio.NewScanner(strings.NewReader(dappfileRenderContent))
	scanner.Split(splitYAMLDocument)

	var docs []*Doc
	var line int
	for scanner.Scan() {
		content := make([]byte, len(scanner.Bytes()))
		copy(content, scanner.Bytes())

		if !emptyDocContent(content) {
			docs = append(docs, &Doc{
				Line:           line,
				Content:        content,
				RenderFilePath: dappfileRenderPath,
			})
		}

		contentLines := bytes.Split(content, []byte("\n"))
		if string(contentLines[len(contentLines)-1]) == "" {
			contentLines = contentLines[0 : len(contentLines)-1]
		}
		line += len(contentLines) + 1
	}

	return docs, nil
}

// TODO: переделать на ParseFiles вместо Parse
func parseDappfileYaml(dappfilePath string) (string, error) {
	data, err := ioutil.ReadFile(dappfilePath)
	if err != nil {
		return "", err
	}

	tmpl := template.New("dappfile")
	tmpl.Funcs(funcMap(tmpl))
	if _, err := tmpl.Parse(string(data)); err != nil {
		return "", err
	}
	return executeTemplate(tmpl, "dappfile", map[string]interface{}{"Files": Files{filepath.Dir(dappfilePath)}})
}

func funcMap(tmpl *template.Template) template.FuncMap {
	funcMap := sprig.TxtFuncMap()
	funcMap["include"] = func(name string, data interface{}) (string, error) {
		return executeTemplate(tmpl, name, data)
	}
	return funcMap
}

func executeTemplate(tmpl *template.Template, name string, data interface{}) (string, error) {
	buf := bytes.NewBuffer(nil)
	if err := tmpl.ExecuteTemplate(buf, name, data); err != nil {
		return "", err
	}
	return buf.String(), nil
}

type Files struct {
	HomePath string
}

func (f Files) Get(path string) string {
	b, err := ioutil.ReadFile(filepath.Join(f.HomePath, path))
	if err != nil {
		return ""
	}
	return string(b)
}

func splitYAMLDocument(data []byte, atEOF bool) (advance int, token []byte, err error) {
	const (
		stateLinebegin   = 0
		stateRegularLine = 1
		stateDocDash1    = 2
		stateDocDash2    = 3
		stateDocDash3    = 4
		stateDocSpaces   = 5
		stateDocComment  = 6
	)

	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	state := stateLinebegin
	var index, docLineSize int
	var ch byte
	for index, ch = range data {
		switch ch {
		case '-':
			switch state {
			case stateLinebegin:
				docLineSize = 0
				state = stateDocDash1
			case stateDocDash1:
				docLineSize += 1
				state = stateDocDash2
			case stateDocDash2:
				docLineSize += 1
				state = stateDocDash3
			default:
				state = stateRegularLine
			}
		case '\n':
			switch state {
			case stateDocDash3, stateDocSpaces, stateDocComment:
				advance = index + 1
				token = data[0 : index-docLineSize-1]
				return advance, token, nil
			default:
				state = stateLinebegin
			}
		case ' ', '\r', '\t':
			switch state {
			case stateDocDash3, stateDocSpaces:
				docLineSize += 1
				state = stateDocSpaces
			case stateDocComment:
				docLineSize += 1
			}
		case '#':
			switch state {
			case stateDocDash3, stateDocSpaces, stateDocComment:
				docLineSize += 1
				state = stateDocComment
			default:
				state = stateRegularLine
			}
		default:
			switch state {
			case stateDocComment:
				docLineSize += 1
			default:
				state = stateRegularLine
			}
		}
	}

	switch state {
	case stateDocDash3, stateDocSpaces, stateDocComment:
		return index + 1, data[0 : index-docLineSize], nil
	default:
		return index + 1, data, nil
	}
}

func emptyDocContent(content []byte) bool {
	const (
		stateRegular = 0
		stateComment = 1
	)

	state := stateRegular
	for _, ch := range content {
		switch ch {
		case '#':
			state = stateComment
		case '\n':
			state = stateRegular
		case ' ', '\r', '\t':
		default:
			if state == stateRegular {
				return false
			}
		}
	}
	return true
}

func splitByDimgs(docs []*Doc, dappfileRenderContent string, dappfileRenderPath string) ([]*Dimg, error) {
	rawDimgs, err := splitByRawDimgs(docs)
	if err != nil {
		return nil, err
	}

	var dimgs []*Dimg
	var artifacts []*DimgArtifact

	for _, rawDimg := range rawDimgs {
		if rawDimg.Type() == "dimgs" {
			if sameDimgs, err := rawDimg.ToDirectives(); err != nil {
				return nil, err
			} else {
				dimgs = append(dimgs, sameDimgs...)
			}
		} else {
			if dimgArtifact, err := rawDimg.ToArtifactDirective(); err != nil {
				return nil, err
			} else {
				artifacts = append(artifacts, dimgArtifact)
			}
		}
	}

	if len(dimgs) == 0 {
		return nil, NewConfigError(fmt.Sprintf("No dimgs defined, at least one dimg required!\n\n%s:\n\n```\n%s```\n", dappfileRenderPath, dappfileRenderContent))
	}

	if err = associateArtifacts(dimgs, artifacts); err != nil {
		return nil, err
	}

	return dimgs, nil
}

func associateArtifacts(dimgs []*Dimg, artifacts []*DimgArtifact) error {
	for _, dimg := range dimgs {
		for _, importArtifact := range dimg.Import {
			if err := importArtifact.AssociateArtifact(artifacts); err != nil {
				return err
			}
		}
	}
	for _, dimg := range artifacts {
		for _, importArtifact := range dimg.Import {
			if err := importArtifact.AssociateArtifact(artifacts); err != nil {
				return err
			}
		}
	}
	return nil
}

func splitByRawDimgs(docs []*Doc) ([]*RawDimg, error) {
	var rawDimgs []*RawDimg
	ParentStack = util.NewStack()
	for _, doc := range docs {
		dimg := &RawDimg{Doc: doc}
		err := yaml.Unmarshal(doc.Content, &dimg)
		if err != nil {
			return nil, newYamlUnmarshalError(err, doc)
		}
		rawDimgs = append(rawDimgs, dimg)
	}

	return rawDimgs, nil
}

func newYamlUnmarshalError(err error, doc *Doc) error {
	switch err.(type) {
	case *ConfigError:
		return err
	default:
		message := err.Error()
		reg, err := regexp.Compile("line ([0-9]+)")
		if err != nil {
			return err
		}

		res := reg.FindStringSubmatch(message)

		if len(res) == 2 {
			line, err := strconv.Atoi(res[1])
			if err != nil {
				return err
			}

			message = reg.ReplaceAllString(message, fmt.Sprintf("line %d", line+doc.Line))
		}
		return NewDetailedConfigError(message, nil, doc)
	}
}
<|endoftext|>


<commit_before>package domain

import (
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/dai"
	"github.com/materials-commons/mcstore/pkg/db/schema"
)

// TODO: Group caching
// TODO: cache reloading

type Access struct {
	groups dai.Groups
	files  dai.Files
	users  dai.Users
}

func NewAccess(groups dai.Groups, files dai.Files, users dai.Users) *Access {
	return &Access{
		groups: groups,
		files:  files,
		users:  users,
	}
}

// AllowedByOwner checks to see if the user making the request has access to the
// particular item. Access is determined as follows:
// 1. If the user and the owner of the item are the same return true (has access).
// 2. Get a list of all the users groups for the item's owner.
//    For each user in the user group see if the requesting user
//    is included. If so then return true (has access).
// 3. None of the above matched - return false (no access)
func (a *Access) AllowedByOwner(owner, user string) bool {
	// Check if user and file owner are the same, or the user is
	// in the admin group.
	if user == owner || a.isAdmin(user) {
		return true
	}

	// Get the owners groups
	groups, err := a.groups.ForOwner(owner)
	if err != nil {
		// Some sort of error occurred, assume no access
		return false
	}

	// For each group go through its list of users and see if
	// they match the requesting user. If there is a match
	// then the owner has given access to the user.
	for _, group := range groups {
		users := group.Users
		for _, u := range users {
			if u == user {
				return true
			}
		}
	}

	return false
}

func (a *Access) isAdmin(user string) bool {
	group, err := a.groups.ByID("admin")
	if err != nil {
		return false
	}

	for _, admin := range group.Users {
		if admin == user {
			return true
		}
	}

	return false
}

func (a *Access) GetFile(apikey, fileID string) (*schema.File, error) {
	user, err := a.users.ByAPIKey(apikey)
	if err != nil {
		// log error here
		app.Log.Error("User lookup failed", "error", err, "apikey", apikey)
		return nil, app.ErrNoAccess
	}

	file, err := a.files.ByID(fileID)
	if err != nil {
		app.Log.Error("File lookup failed", "error", err, "fileid", fileID)
		return nil, app.ErrNoAccess
	}

	if !a.AllowedByOwner(file.Owner, user.ID) {
		app.Log.Info("Access denied", "fileid", file.ID, "user", user.ID)
		return nil, app.ErrNoAccess
	}

	return file, nil
}
<commit_msg>Fix up comments.<commit_after>package domain

import (
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/dai"
	"github.com/materials-commons/mcstore/pkg/db/schema"
)

// TODO: Group caching
// TODO: cache reloading

// Access validates access to data. It checks if a user
// has been given permission to access a particular item.
type Access struct {
	groups dai.Groups
	files  dai.Files
	users  dai.Users
}

// NewAccess creates a new Access.
func NewAccess(groups dai.Groups, files dai.Files, users dai.Users) *Access {
	return &Access{
		groups: groups,
		files:  files,
		users:  users,
	}
}

// AllowedByOwner checks to see if the user making the request has access to the
// particular item. Access is determined as follows:
// 1. If the user and the owner of the item are the same
//    or the user is in the admin group return true (has access).
// 2. Get a list of all the users groups for the item's owner.
//    For each user in the user group see if the requesting user
//    is included. If so then return true (has access).
// 3. None of the above matched - return false (no access).
func (a *Access) AllowedByOwner(owner, user string) bool {
	// Check if user and file owner are the same, or the user is
	// in the admin group.
	if user == owner || a.isAdmin(user) {
		return true
	}

	// Get the owners groups
	groups, err := a.groups.ForOwner(owner)
	if err != nil {
		// Some sort of error occurred, assume no access
		return false
	}

	// For each group go through its list of users and see if
	// they match the requesting user. If there is a match
	// then the owner has given access to the user.
	for _, group := range groups {
		users := group.Users
		for _, u := range users {
			if u == user {
				return true
			}
		}
	}

	return false
}

// isAdmin checks if a user is in the admin group.
func (a *Access) isAdmin(user string) bool {
	group, err := a.groups.ByID("admin")
	if err != nil {
		return false
	}

	for _, admin := range group.Users {
		if admin == user {
			return true
		}
	}

	return false
}

// GetFile will validate access to a file. Rather than taking a user,
// it takes an apikey and looks up the user. It returns the file if
// access has been granted, otherwise it returns the erro ErrNoAccess.
func (a *Access) GetFile(apikey, fileID string) (*schema.File, error) {
	user, err := a.users.ByAPIKey(apikey)
	if err != nil {
		// log error here
		app.Log.Error("User lookup failed", "error", err, "apikey", apikey)
		return nil, app.ErrNoAccess
	}

	file, err := a.files.ByID(fileID)
	if err != nil {
		app.Log.Error("File lookup failed", "error", err, "fileid", fileID)
		return nil, app.ErrNoAccess
	}

	if !a.AllowedByOwner(file.Owner, user.ID) {
		app.Log.Info("Access denied", "fileid", file.ID, "user", user.ID)
		return nil, app.ErrNoAccess
	}

	return file, nil
}
<|endoftext|>