<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package dns

import (
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/digitalocean/godo"
	"github.com/digitalocean/godo/context"

	"k8s.io/kubernetes/federation/pkg/dnsprovider"
	"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)

const ipPlaceholder = "203.0.113.123"

// DNS implements dnsprovider.Interface
type DNS struct {
	client *godo.Client
}

// NewProvider returns an implementation of dnsprovider.Interface
func NewProvider(client *godo.Client) dnsprovider.Interface {
	return &DNS{client: client}
}

// Zones returns an implementation of dnsprovider.Zones
func (d *DNS) Zones() (dnsprovider.Zones, bool) {
	return &zones{
		client: d.client,
	}, true
}

// zones is an implementation of dnsprovider.Zones
type zones struct {
	client *godo.Client
}

// List returns a list of all dns zones
func (z *zones) List() ([]dnsprovider.Zone, error) {
	domains, err := listDomains(z.client)
	if err != nil {
		return nil, err
	}

	var newZone *zone
	var zones []dnsprovider.Zone
	for _, domain := range domains {
		newZone = &zone{
			name:   domain.Name,
			client: z.client,
		}
		zones = append(zones, newZone)
	}

	return zones, nil
}

// Add adds a new DNS zone
func (z *zones) Add(newZone dnsprovider.Zone) (dnsprovider.Zone, error) {
	domainCreateRequest := &godo.DomainCreateRequest{
		Name:      newZone.Name(),
		IPAddress: ipPlaceholder,
	}

	domain, err := createDomain(z.client, domainCreateRequest)
	if err != nil {
		return nil, err
	}

	return &zone{
		name:   domain.Name,
		client: z.client,
	}, nil
}

// Remove deletes a zone
func (z *zones) Remove(zone dnsprovider.Zone) error {
	return deleteDomain(z.client, zone.Name())
}

// New returns a new implementation of dnsprovider.Zone
func (z *zones) New(name string) (dnsprovider.Zone, error) {
	return &zone{
		name:   name,
		client: z.client,
	}, nil

}

// zone implements dnsprovider.Zone
type zone struct {
	name   string
	client *godo.Client
}

// Name returns the Name of a dns zone
func (z *zone) Name() string {
	return z.name
}

// ID returns the name of a dns zone, in DO the ID is the name
func (z *zone) ID() string {
	return z.name
}

// ResourceRecordSet returns an implementation of dnsprovider.ResourceRecordSets
func (z *zone) ResourceRecordSets() (dnsprovider.ResourceRecordSets, bool) {
	return &resourceRecordSets{zone: z, client: z.client}, true
}

// resourceRecordSets implements dnsprovider.ResourceRecordSet
type resourceRecordSets struct {
	zone   *zone
	client *godo.Client
}

// List returns a list of dnsprovider.ResourceRecordSet
func (r *resourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) {
	records, err := getRecords(r.client, r.zone.Name())
	if err != nil {
		return nil, err
	}

	var rrset *resourceRecordSet
	var rrsets []dnsprovider.ResourceRecordSet
	for _, record := range records {
		rrset = &resourceRecordSet{
			name:       record.Name,
			data:       record.Data,
			ttl:        record.TTL,
			recordType: rrstype.RrsType(record.Type),
		}

		rrsets = append(rrsets, rrset)
	}

	return rrsets, nil

}

// Get returns a list of dnsprovider.ResourceRecordSet that matches the name parameter
func (r *resourceRecordSets) Get(name string) ([]dnsprovider.ResourceRecordSet, error) {
	records, err := r.List()
	if err != nil {
		return nil, err
	}

	var recordSets []dnsprovider.ResourceRecordSet
	for _, record := range records {
		if record.Name() == name {
			recordSets = append(recordSets, record)
		}
	}

	return recordSets, nil
}

// New returns an implementation of dnsprovider.ResourceRecordSet
func (r *resourceRecordSets) New(name string, rrdatas []string, ttl int64, rrstype rrstype.RrsType) dnsprovider.ResourceRecordSet {
	if len(rrdatas) > 1 {
		return nil
	}

	return &resourceRecordSet{
		name:       name,
		data:       rrdatas[0],
		ttl:        int(ttl),
		recordType: rrstype,
	}
}

// StartChangeset returns an implementation of dnsprovider.ResourceRecordChangeset
func (r *resourceRecordSets) StartChangeset() dnsprovider.ResourceRecordChangeset {
	return &resourceRecordChangeset{
		client:    r.client,
		zone:      r.zone,
		rrsets:    r,
		additions: []dnsprovider.ResourceRecordSet{},
		removals:  []dnsprovider.ResourceRecordSet{},
		upserts:   []dnsprovider.ResourceRecordSet{},
	}
}

// Zone returns the associated implementation of dnsprovider.Zone
func (r *resourceRecordSets) Zone() dnsprovider.Zone {
	return r.zone
}

// recordRecordSet implements dnsprovider.ResourceRecordSet which represents
// a single record associated with a zone
type resourceRecordSet struct {
	name       string
	data       string
	ttl        int
	recordType rrstype.RrsType
}

// Name returns the name of a resource record set
func (r *resourceRecordSet) Name() string {
	return r.name
}

// Rrdatas returns a list of data associated with a resource record set
// in DO this is almost always the IP of a record
func (r *resourceRecordSet) Rrdatas() []string {
	return []string{r.data}
}

// Ttl returns the time-to-live of a record
func (r *resourceRecordSet) Ttl() int64 {
	return int64(r.ttl)
}

// Type returns the type of record a resource record set is
func (r *resourceRecordSet) Type() rrstype.RrsType {
	return r.recordType
}

// resourceRecordChangeset implements dnsprovider.ResourceRecordChangeset
type resourceRecordChangeset struct {
	client *godo.Client
	zone   *zone
	rrsets dnsprovider.ResourceRecordSets

	additions []dnsprovider.ResourceRecordSet
	removals  []dnsprovider.ResourceRecordSet
	upserts   []dnsprovider.ResourceRecordSet
}

// Add adds a new resource record set to the list of additions to apply
func (r *resourceRecordChangeset) Add(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
	r.additions = append(r.additions, rrset)
	return r
}

// Remove adds a new resource record set to the list of removals to apply
func (r *resourceRecordChangeset) Remove(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
	r.removals = append(r.removals, rrset)
	return r
}

// Upsert adds a new resource record set to the list of upesrts to apply
func (r *resourceRecordChangeset) Upsert(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
	r.upserts = append(r.upserts, rrset)
	return r
}

// Apply adds new records stored in r.additions, updates records stored
// in r.upserts and deletes records stored in r.removals
func (r *resourceRecordChangeset) Apply() error {
	if len(r.additions) > 0 {
		for _, record := range r.additions {
			recordCreateRequest := &godo.DomainRecordEditRequest{
				Name: record.Name(),
				Data: record.Rrdatas()[0],
				TTL:  int(record.Ttl()),
				Type: string(record.Type()),
			}
			err := createRecord(r.client, r.zone.Name(), recordCreateRequest)
			if err != nil {
				return fmt.Errorf("could not create record: %v", err)
			}
		}
	}

	if len(r.removals) > 0 {
		records, err := getRecords(r.client, r.zone.Name())
		if err != nil {
			return err
		}

		for _, record := range r.removals {
			var desiredRecord godo.DomainRecord
			found := false
			for _, domainRecord := range records {
				if domainRecord.Name == record.Name() {
					desiredRecord = domainRecord
					found = true
				}
			}
			if !found {
				return fmt.Errorf("could not find desired record to remove")
			}

			err := deleteRecord(r.client, r.zone.Name(), desiredRecord.ID)
			if err != nil {
				return err
			}
		}

	}

	if len(r.upserts) > 0 {
		records, err := getRecords(r.client, r.zone.Name())
		if err != nil {
			return err
		}

		for _, record := range r.upserts {
			var desiredRecord godo.DomainRecord
			found := false
			for _, domainRecord := range records {
				if domainRecord.Name == record.Name() {
					desiredRecord = domainRecord
					found = true
				}
			}

			if !found {
				return fmt.Errorf("could not find desired record to upsert")
			}

			domainEditRequest := &godo.DomainRecordEditRequest{
				Name: record.Name(),
				Data: record.Rrdatas()[0],
				TTL:  int(record.Ttl()),
				Type: string(record.Type()),
			}
			err := editRecord(r.client, r.zone.Name(), desiredRecord.ID, domainEditRequest)
			if err != nil {
				return err
			}
		}

	}

	return nil
}

// IsEmpty returns true if a changeset is empty, false otherwise
func (r *resourceRecordChangeset) IsEmpty() bool {
	if len(r.additions) == 0 && len(r.removals) == 0 && len(r.upserts) == 0 {
		return true
	}

	return false
}

// ResourceRecordSet returns the associated resourceRecordSets of a changset
func (r *resourceRecordChangeset) ResourceRecordSets() dnsprovider.ResourceRecordSets {
	return r.rrsets
}

// listDomains returns a list of godo.Domain
func listDomains(c *godo.Client) ([]godo.Domain, error) {
	// TODO (andrewsykim): pagination in ListOptions
	domains, resp, err := c.Domains.List(context.TODO(), &godo.ListOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to list domains: %v", err)
	}

	if resp.StatusCode != 200 {
		respData, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("error reading response body: %v", err)
		}

		return nil, fmt.Errorf("received non 200 status code: %d from api: %v",
			resp.StatusCode, string(respData))
	}

	return domains, err
}

// createDomain creates a domain provided godo.DomainCreateRequest
func createDomain(c *godo.Client, createRequest *godo.DomainCreateRequest) (*godo.Domain, error) {
	domain, resp, err := c.Domains.Create(context.TODO(), createRequest)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		respData, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("error reading response body: %v", err)
		}

		return nil, fmt.Errorf("received non 200 status code: %d from api: %v",
			resp.StatusCode, string(respData))
	}

	return domain, nil
}

// deleteDomain deletes a domain given its name
func deleteDomain(c *godo.Client, name string) error {
	resp, err := c.Domains.Delete(context.TODO(), name)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		respData, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("error reading response body: %v", err)
		}

		return fmt.Errorf("received non 200 status code: %d from api: %v",
			resp.StatusCode, string(respData))
	}

	return nil
}

// getRecords returns a list of godo.DomainRecord given a zone name
func getRecords(c *godo.Client, zoneName string) ([]godo.DomainRecord, error) {
	records, resp, err := c.Domains.Records(context.TODO(), zoneName, &godo.ListOptions{})
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		respData, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("error reading response body: %v", err)
		}

		return nil, fmt.Errorf("received non 200 status code: %d from api: %v",
			resp.StatusCode, string(respData))
	}

	return records, nil
}

// createRecord creates a record given an assoicated zone and a godo.DomainRecordEditRequest
func createRecord(c *godo.Client, zoneName string, createRequest *godo.DomainRecordEditRequest) error {
	_, resp, err := c.Domains.CreateRecord(context.TODO(), zoneName, createRequest)
	if err != nil {
		return fmt.Errorf("error applying changeset: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		respData, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("error reading response body: %v", err)
		}

		return fmt.Errorf("received non 200 status code: %d from api: %v",
			resp.StatusCode, string(respData))
	}

	return nil
}

// editRecord edits a record given an associated ozone and a godo.DomainRecordEditRequest
func editRecord(c *godo.Client, zoneName string, recordID int, editRequest *godo.DomainRecordEditRequest) error {
	_, resp, err := c.Domains.EditRecord(context.TODO(), zoneName, recordID, editRequest)
	if err != nil {
		return fmt.Errorf("error applying changeset: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		respData, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("error reading response body: %v", err)
		}

		return fmt.Errorf("received non 200 status code: %d from api: %v",
			resp.StatusCode, string(respData))
	}

	return nil
}

// deleteRecord deletes a record given an associated zone and a record ID
func deleteRecord(c *godo.Client, zoneName string, recordID int) error {
	resp, err := c.Domains.DeleteRecord(context.TODO(), zoneName, recordID)
	if err != nil {
		return fmt.Errorf("error applying changeset: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		respData, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("error reading response body: %v", err)
		}

		return fmt.Errorf("received non 200 status code: %d from api: %v",
			resp.StatusCode, string(respData))
	}

	return nil
}
<commit_msg>add response handling function<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package dns

import (
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/digitalocean/godo"
	"github.com/digitalocean/godo/context"

	"k8s.io/kubernetes/federation/pkg/dnsprovider"
	"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)

const ipPlaceholder = "203.0.113.123"

// DNS implements dnsprovider.Interface
type DNS struct {
	client *godo.Client
}

// NewProvider returns an implementation of dnsprovider.Interface
func NewProvider(client *godo.Client) dnsprovider.Interface {
	return &DNS{client: client}
}

// Zones returns an implementation of dnsprovider.Zones
func (d *DNS) Zones() (dnsprovider.Zones, bool) {
	return &zones{
		client: d.client,
	}, true
}

// zones is an implementation of dnsprovider.Zones
type zones struct {
	client *godo.Client
}

// List returns a list of all dns zones
func (z *zones) List() ([]dnsprovider.Zone, error) {
	domains, err := listDomains(z.client)
	if err != nil {
		return nil, err
	}

	var newZone *zone
	var zones []dnsprovider.Zone
	for _, domain := range domains {
		newZone = &zone{
			name:   domain.Name,
			client: z.client,
		}
		zones = append(zones, newZone)
	}

	return zones, nil
}

// Add adds a new DNS zone
func (z *zones) Add(newZone dnsprovider.Zone) (dnsprovider.Zone, error) {
	domainCreateRequest := &godo.DomainCreateRequest{
		Name:      newZone.Name(),
		IPAddress: ipPlaceholder,
	}

	domain, err := createDomain(z.client, domainCreateRequest)
	if err != nil {
		return nil, err
	}

	return &zone{
		name:   domain.Name,
		client: z.client,
	}, nil
}

// Remove deletes a zone
func (z *zones) Remove(zone dnsprovider.Zone) error {
	return deleteDomain(z.client, zone.Name())
}

// New returns a new implementation of dnsprovider.Zone
func (z *zones) New(name string) (dnsprovider.Zone, error) {
	return &zone{
		name:   name,
		client: z.client,
	}, nil

}

// zone implements dnsprovider.Zone
type zone struct {
	name   string
	client *godo.Client
}

// Name returns the Name of a dns zone
func (z *zone) Name() string {
	return z.name
}

// ID returns the name of a dns zone, in DO the ID is the name
func (z *zone) ID() string {
	return z.name
}

// ResourceRecordSet returns an implementation of dnsprovider.ResourceRecordSets
func (z *zone) ResourceRecordSets() (dnsprovider.ResourceRecordSets, bool) {
	return &resourceRecordSets{zone: z, client: z.client}, true
}

// resourceRecordSets implements dnsprovider.ResourceRecordSet
type resourceRecordSets struct {
	zone   *zone
	client *godo.Client
}

// List returns a list of dnsprovider.ResourceRecordSet
func (r *resourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) {
	records, err := getRecords(r.client, r.zone.Name())
	if err != nil {
		return nil, err
	}

	var rrset *resourceRecordSet
	var rrsets []dnsprovider.ResourceRecordSet
	for _, record := range records {
		rrset = &resourceRecordSet{
			name:       record.Name,
			data:       record.Data,
			ttl:        record.TTL,
			recordType: rrstype.RrsType(record.Type),
		}

		rrsets = append(rrsets, rrset)
	}

	return rrsets, nil

}

// Get returns a list of dnsprovider.ResourceRecordSet that matches the name parameter
func (r *resourceRecordSets) Get(name string) ([]dnsprovider.ResourceRecordSet, error) {
	records, err := r.List()
	if err != nil {
		return nil, err
	}

	var recordSets []dnsprovider.ResourceRecordSet
	for _, record := range records {
		if record.Name() == name {
			recordSets = append(recordSets, record)
		}
	}

	return recordSets, nil
}

// New returns an implementation of dnsprovider.ResourceRecordSet
func (r *resourceRecordSets) New(name string, rrdatas []string, ttl int64, rrstype rrstype.RrsType) dnsprovider.ResourceRecordSet {
	if len(rrdatas) > 1 {
		return nil
	}

	return &resourceRecordSet{
		name:       name,
		data:       rrdatas[0],
		ttl:        int(ttl),
		recordType: rrstype,
	}
}

// StartChangeset returns an implementation of dnsprovider.ResourceRecordChangeset
func (r *resourceRecordSets) StartChangeset() dnsprovider.ResourceRecordChangeset {
	return &resourceRecordChangeset{
		client:    r.client,
		zone:      r.zone,
		rrsets:    r,
		additions: []dnsprovider.ResourceRecordSet{},
		removals:  []dnsprovider.ResourceRecordSet{},
		upserts:   []dnsprovider.ResourceRecordSet{},
	}
}

// Zone returns the associated implementation of dnsprovider.Zone
func (r *resourceRecordSets) Zone() dnsprovider.Zone {
	return r.zone
}

// recordRecordSet implements dnsprovider.ResourceRecordSet which represents
// a single record associated with a zone
type resourceRecordSet struct {
	name       string
	data       string
	ttl        int
	recordType rrstype.RrsType
}

// Name returns the name of a resource record set
func (r *resourceRecordSet) Name() string {
	return r.name
}

// Rrdatas returns a list of data associated with a resource record set
// in DO this is almost always the IP of a record
func (r *resourceRecordSet) Rrdatas() []string {
	return []string{r.data}
}

// Ttl returns the time-to-live of a record
func (r *resourceRecordSet) Ttl() int64 {
	return int64(r.ttl)
}

// Type returns the type of record a resource record set is
func (r *resourceRecordSet) Type() rrstype.RrsType {
	return r.recordType
}

// resourceRecordChangeset implements dnsprovider.ResourceRecordChangeset
type resourceRecordChangeset struct {
	client *godo.Client
	zone   *zone
	rrsets dnsprovider.ResourceRecordSets

	additions []dnsprovider.ResourceRecordSet
	removals  []dnsprovider.ResourceRecordSet
	upserts   []dnsprovider.ResourceRecordSet
}

// Add adds a new resource record set to the list of additions to apply
func (r *resourceRecordChangeset) Add(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
	r.additions = append(r.additions, rrset)
	return r
}

// Remove adds a new resource record set to the list of removals to apply
func (r *resourceRecordChangeset) Remove(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
	r.removals = append(r.removals, rrset)
	return r
}

// Upsert adds a new resource record set to the list of upesrts to apply
func (r *resourceRecordChangeset) Upsert(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
	r.upserts = append(r.upserts, rrset)
	return r
}

// Apply adds new records stored in r.additions, updates records stored
// in r.upserts and deletes records stored in r.removals
func (r *resourceRecordChangeset) Apply() error {
	if len(r.additions) > 0 {
		for _, record := range r.additions {
			recordCreateRequest := &godo.DomainRecordEditRequest{
				Name: record.Name(),
				Data: record.Rrdatas()[0],
				TTL:  int(record.Ttl()),
				Type: string(record.Type()),
			}
			err := createRecord(r.client, r.zone.Name(), recordCreateRequest)
			if err != nil {
				return fmt.Errorf("could not create record: %v", err)
			}
		}
	}

	if len(r.removals) > 0 {
		records, err := getRecords(r.client, r.zone.Name())
		if err != nil {
			return err
		}

		for _, record := range r.removals {
			var desiredRecord godo.DomainRecord
			found := false
			for _, domainRecord := range records {
				if domainRecord.Name == record.Name() {
					desiredRecord = domainRecord
					found = true
				}
			}
			if !found {
				return fmt.Errorf("could not find desired record to remove")
			}

			err := deleteRecord(r.client, r.zone.Name(), desiredRecord.ID)
			if err != nil {
				return err
			}
		}

	}

	if len(r.upserts) > 0 {
		records, err := getRecords(r.client, r.zone.Name())
		if err != nil {
			return err
		}

		for _, record := range r.upserts {
			var desiredRecord godo.DomainRecord
			found := false
			for _, domainRecord := range records {
				if domainRecord.Name == record.Name() {
					desiredRecord = domainRecord
					found = true
				}
			}

			if !found {
				return fmt.Errorf("could not find desired record to upsert")
			}

			domainEditRequest := &godo.DomainRecordEditRequest{
				Name: record.Name(),
				Data: record.Rrdatas()[0],
				TTL:  int(record.Ttl()),
				Type: string(record.Type()),
			}
			err := editRecord(r.client, r.zone.Name(), desiredRecord.ID, domainEditRequest)
			if err != nil {
				return err
			}
		}

	}

	return nil
}

// IsEmpty returns true if a changeset is empty, false otherwise
func (r *resourceRecordChangeset) IsEmpty() bool {
	if len(r.additions) == 0 && len(r.removals) == 0 && len(r.upserts) == 0 {
		return true
	}

	return false
}

// ResourceRecordSet returns the associated resourceRecordSets of a changset
func (r *resourceRecordChangeset) ResourceRecordSets() dnsprovider.ResourceRecordSets {
	return r.rrsets
}

// listDomains returns a list of godo.Domain
func listDomains(c *godo.Client) ([]godo.Domain, error) {
	// TODO (andrewsykim): pagination in ListOptions
	domains, resp, err := c.Domains.List(context.TODO(), &godo.ListOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to list domains: %v", err)
	}

	if err = handleResponse(resp); err != nil {
		return nil, err
	}

	return domains, err
}

// createDomain creates a domain provided godo.DomainCreateRequest
func createDomain(c *godo.Client, createRequest *godo.DomainCreateRequest) (*godo.Domain, error) {
	domain, resp, err := c.Domains.Create(context.TODO(), createRequest)
	if err != nil {
		return nil, err
	}

	if err = handleResponse(resp); err != nil {
		return nil, err
	}

	return domain, nil
}

// deleteDomain deletes a domain given its name
func deleteDomain(c *godo.Client, name string) error {
	resp, err := c.Domains.Delete(context.TODO(), name)
	if err != nil {
		return err
	}

	if err = handleResponse(resp); err != nil {
		return err
	}

	return nil
}

// getRecords returns a list of godo.DomainRecord given a zone name
func getRecords(c *godo.Client, zoneName string) ([]godo.DomainRecord, error) {
	records, resp, err := c.Domains.Records(context.TODO(), zoneName, &godo.ListOptions{})
	if err != nil {
		return nil, err
	}

	if err = handleResponse(resp); err != nil {
		return nil, err
	}

	return records, nil
}

// createRecord creates a record given an assoicated zone and a godo.DomainRecordEditRequest
func createRecord(c *godo.Client, zoneName string, createRequest *godo.DomainRecordEditRequest) error {
	_, resp, err := c.Domains.CreateRecord(context.TODO(), zoneName, createRequest)
	if err != nil {
		return fmt.Errorf("error applying changeset: %v", err)
	}

	if err = handleResponse(resp); err != nil {
		return err
	}

	return nil
}

// editRecord edits a record given an associated ozone and a godo.DomainRecordEditRequest
func editRecord(c *godo.Client, zoneName string, recordID int, editRequest *godo.DomainRecordEditRequest) error {
	_, resp, err := c.Domains.EditRecord(context.TODO(), zoneName, recordID, editRequest)
	if err != nil {
		return fmt.Errorf("error applying changeset: %v", err)
	}

	if err = handleResponse(resp); err != nil {
		return err
	}

	return nil
}

// deleteRecord deletes a record given an associated zone and a record ID
func deleteRecord(c *godo.Client, zoneName string, recordID int) error {
	resp, err := c.Domains.DeleteRecord(context.TODO(), zoneName, recordID)
	if err != nil {
		return fmt.Errorf("error applying changeset: %v", err)
	}

	if err = handleResponse(resp); err != nil {
		return err
	}

	return nil
}

func handleResponse(resp *godo.Response) error {
	if resp.StatusCode != http.StatusOK {
		respData, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("error reading response body: %v", err)
		}

		return fmt.Errorf("received non 200 status code: %d from api: %v",
			resp.StatusCode, string(respData))
	}

	return nil
}
<|endoftext|>


<commit_before>package admin

import (
	"encoding/json"
	"github.com/fritzpay/paymentd/pkg/paymentd/principal"
	"gopkg.in/inconshreveable/log15.v2"
	"net/http"
	"path"
	"strings"
)

const (
	APIParamPrincipalName = "principalName"
)

func (a *API) PrincipalRequest() http.Handler {

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")

		log := a.log.New(log15.Ctx{"method": "Principal Request"})
		log.Info("Method:" + r.Method)

		if r.Method == "GET" {
			// get principal by name
			principalName := strings.TrimLeft(r.RequestURI, path.Dir(r.RequestURI))
			if len(principalName) < 1 {
				w.WriteHeader(http.StatusInternalServerError)
				log.Info("principalName missing")
				return
			}
			log.Info("Param principalName " + principalName)

			pr, err := principal.PrincipalByNameDB(a.ctx.PrincipalDB(), principalName)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				log.Error("DB get by name failed.", log15.Ctx{"err": err})
				return
			}
			je := json.NewEncoder(w)
			err = je.Encode(&pr)

		} else if r.Method == "PUT" {
			// create new principal
			jd := json.NewDecoder(r.Body)
			pr := principal.Principal{}
			err := jd.Decode(&pr)
			r.Body.Close()
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				log.Error("json decode failed", log15.Ctx{"err": err})

				return
			}

			db := a.ctx.PrincipalDB()
			err = principal.InsertPrincipalDB(db, &pr)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				log.Error("DB insert failed.", log15.Ctx{"err": err})
				return
			}

			pr, err = principal.PrincipalByNameDB(db, pr.Name)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				log.Error("DB get by name failed.", log15.Ctx{"err": err})
				return
			}

			je := json.NewEncoder(w)
			err = je.Encode(&pr)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				log.Error("json encode failed.", log15.Ctx{"err": err})
				return
			}

		} else {
			w.WriteHeader(http.StatusBadRequest)
		}
	})
}
<commit_msg>adjusted the status, added a db entry exist check before insert.<commit_after>package admin

import (
	"encoding/json"
	"github.com/fritzpay/paymentd/pkg/paymentd/principal"
	"gopkg.in/inconshreveable/log15.v2"
	"net/http"
	"path"
	"strings"
)

const (
	APIParamPrincipalName = "principalName"
)

func (a *API) PrincipalRequest() http.Handler {

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")

		log := a.log.New(log15.Ctx{"method": "Principal Request"})
		log.Info("Method:" + r.Method)

		if r.Method == "GET" {
			// get principal by name
			principalName := strings.TrimLeft(r.RequestURI, path.Dir(r.RequestURI))
			if len(principalName) < 1 {
				w.WriteHeader(http.StatusBadRequest)
				log.Info("principalName missing")
				return
			}

			pr, err := principal.PrincipalByNameDB(a.ctx.PrincipalDB(), principalName)
			if err != nil {
				w.WriteHeader(http.StatusNotFound)
				log.Error("DB get by name failed.", log15.Ctx{"err": err})
				return
			}
			je := json.NewEncoder(w)
			err = je.Encode(&pr)

		} else if r.Method == "PUT" {
			// create new principal
			jd := json.NewDecoder(r.Body)
			pr := principal.Principal{}
			err := jd.Decode(&pr)
			r.Body.Close()
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				log.Error("json decode failed", log15.Ctx{"err": err})

				return
			}

			db := a.ctx.PrincipalDB()
			_, err = principal.PrincipalByNameDB(db, pr.Name)
			if err == principal.ErrPrincipalNotFound {
				// insert if not exists
				err = principal.InsertPrincipalDB(db, &pr)
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					log.Error("DB insert failed.", log15.Ctx{"err": err})
					return
				}
			} else if err != nil {
				// other db error
				w.WriteHeader(http.StatusInternalServerError)
				log.Error("DB get by name failed.", log15.Ctx{"err": err})
				return
			} else {
				// already exists
				w.WriteHeader(http.StatusConflict)
				log.Warn("principal already exist: " + pr.Name)
				return
			}

			pr, err = principal.PrincipalByNameDB(db, pr.Name)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				log.Error("DB get by name failed.", log15.Ctx{"err": err})
				return
			}

			je := json.NewEncoder(w)
			err = je.Encode(&pr)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				log.Error("json encode failed.", log15.Ctx{"err": err})
				return
			}

		} else {
			w.WriteHeader(http.StatusBadRequest)
		}
	})
}
<|endoftext|>


<commit_before>package bird

import (
	"regexp"
	"strconv"
	"strings"
)

type Parsed map[string]interface{}

func emptyLine(line string) bool {
	return len(strings.TrimSpace(line)) == 0
}

func getLinesUnfiltered(input string) []string {
	line_sep := regexp.MustCompile(`((\r?\n)|(\r\n?))`)
	return line_sep.Split(input, -1)
}

func getLinesFromString(input string) []string {
	lines := getLinesUnfiltered(input)

	var filtered []string

	for _, line := range lines {
		if !emptyLine(line) {
			filtered = append(filtered, line)
		}
	}

	return filtered
}

func getLines(input []byte) []string {
	return getLinesFromString(string(input))
}

func specialLine(line string) bool {
	return (strings.HasPrefix(line, "BIRD") ||
		strings.HasPrefix(line, "Access restricted"))
}

func parseStatus(input []byte) Parsed {
	res := Parsed{}
	lines := getLines(input)

	start_line_rx := regexp.MustCompile(`^BIRD\s([0-9\.]+)\s*$`)
	router_id_rx := regexp.MustCompile(`^Router\sID\sis\s([0-9\.]+)\s*$`)
	current_server_rx := regexp.MustCompile(`^Current\sserver\stime\sis\s([0-9\-]+)\s([0-9\:]+)\s*$`)
	last_reboot_rx := regexp.MustCompile(`^Last\sreboot\son\s([0-9\-]+)\s([0-9\:]+)\s*$`)
	last_reconfig_rx := regexp.MustCompile(`^Last\sreconfiguration\son\s([0-9\-]+)\s([0-9\:]+)\s*$`)

	for _, line := range lines {
		if start_line_rx.MatchString(line) {
			res["version"] = start_line_rx.FindStringSubmatch(line)[1]
		} else if router_id_rx.MatchString(line) {
			res["router_id"] = router_id_rx.FindStringSubmatch(line)[1]
		} else if current_server_rx.MatchString(line) {
			res["current_server"] = current_server_rx.FindStringSubmatch(line)[1]
		} else if last_reboot_rx.MatchString(line) {
			res["last_reboot"] = last_reboot_rx.FindStringSubmatch(line)[1]
		} else if last_reconfig_rx.MatchString(line) {
			res["last_reconfig"] = last_reconfig_rx.FindStringSubmatch(line)[1]
		} else {
			res["message"] = line
		}
	}
	return Parsed{"status": res}
}

func parseProtocols(input []byte) Parsed {
	res := Parsed{}
	protocols := []string{}
	lines := getLinesUnfiltered(string(input))

	proto := ""
	for _, line := range lines {
		if emptyLine(line) {
			if !emptyLine(proto) {
				protocols = append(protocols, proto)
			}
			proto = ""
		} else {
			proto += (line + "\n")
		}
	}

	res["protocols"] = protocols
	return res
}

func parseSymbols(input []byte) Parsed {
	res := Parsed{}
	lines := getLines(input)

	key_rx := regexp.MustCompile(`^([^\s]+)\s+(.+)\s*$`)
	for _, line := range lines {
		if specialLine(line) {
			continue
		}

		if key_rx.MatchString(line) {
			groups := key_rx.FindStringSubmatch(line)
			res[groups[2]] = groups[1]
		}
	}

	return Parsed{"symbols": res}
}

func mainRouteDetail(groups []string, route Parsed) Parsed {
	route["network"] = groups[1]
	route["gateway"] = groups[2]
	route["interface"] = groups[3]
	route["from_protocol"] = groups[4]
	route["age"] = groups[5]
	route["learnt_from"] = groups[6]
	route["primary"] = groups[7] == "*"
	route["metric"] = parseInt(groups[8])
	return route
}

func parseRoutes(input []byte) Parsed {
	res := Parsed{}
	lines := getLines(input)

	routes := []Parsed{}

	route := Parsed{}
	start_def_rx := regexp.MustCompile(`^([0-9a-f\.\:\/]+)\s+via\s+([0-9a-f\.\:]+)\s+on\s+(\w+)\s+\[(\w+)\s+([0-9\-\:]+)(?:\s+from\s+([0-9a-f\.\:\/]+)){0,1}\]\s+(?:(\*)\s+){0,1}\((\d+)(?:\/\d+){0,1}\).*$`)
	second_rx := regexp.MustCompile(`^\s+via\s+([0-9a-f\.\:]+)\s+on\s+(\w+)\s+\[(\w+)\s+([0-9\-\:]+)(?:\s+from\s+([0-9a-f\.\:\/]+)){0,1}\]\s+(?:(\*)\s+){0,1}\((\d+)(?:\/\d+){0,1}\).*$`)
	type_rx := regexp.MustCompile(`^\s+Type:\s+(.*)\s*$`)
	bgp_rx := regexp.MustCompile(`^\s+BGP.(\w+):\s+(\w+)\s*$`)
	community_rx := regexp.MustCompile(`^\((\d+),(\d+)\)`)
	for _, line := range lines {
		if specialLine(line) || (len(route) == 0 && emptyLine(line)) {
			continue
		}

		if start_def_rx.MatchString(line) {
			if len(route) > 0 {
				routes = append(routes, route)
				route = Parsed{}
			}
			route = mainRouteDetail(start_def_rx.FindStringSubmatch(line), route)
		} else if second_rx.MatchString(line) {
			routes = append(routes, route)
			var network string
			if tmp, ok := route["network"]; ok {
				if val, ok := tmp.(string); ok {
					network = val
				} else {
					continue
				}
			} else {
				continue
			}
			route = Parsed{}

			groups := second_rx.FindStringSubmatch(line)
			first, groups := groups[0], groups[1:]
			groups = append([]string{network}, groups...)
			groups = append([]string{first}, groups...)
			route = mainRouteDetail(groups, route)
		} else if type_rx.MatchString(line) {
			submatch := type_rx.FindStringSubmatch(line)[1]
			route["type"] = strings.Split(submatch, " ")
		} else if bgp_rx.MatchString(line) {
			groups := bgp_rx.FindStringSubmatch(line)
			bgp := Parsed{}

			if tmp, ok := route["bgp"]; ok {
				if val, ok := tmp.(Parsed); ok {
					bgp = val
				}
			}

			if groups[1] == "community" {
				communities := [][]int64{}
				for _, community := range strings.Split(groups[2], " ") {
					if community_rx.MatchString(community) {
						com_groups := community_rx.FindStringSubmatch(community)
						maj := parseInt(com_groups[1])
						min := parseInt(com_groups[2])
						communities = append(communities, []int64{maj, min})
					}
				}
				bgp["communities"] = communities
			} else {
				bgp[groups[1]] = groups[2]
			}

			route["bgp"] = bgp
		}
	}

	if len(route) > 0 {
		routes = append(routes, route)
	}

	res["routes"] = routes
	return res
}

func parseRoutesCount(input []byte) Parsed {
	res := Parsed{}
	lines := getLines(input)

	count_rx := regexp.MustCompile(`^(\d+)\s+of\s+(\d+)\s+routes.*$`)
	for _, line := range lines {
		if specialLine(line) {
			continue
		}

		if count_rx.MatchString(line) {
			count := count_rx.FindStringSubmatch(line)[1]
			res["routes"] = parseInt(count)
		}
	}

	return res
}

// Will snake_case a value like that:
// I am a Weird stRiNg -> i_am_a_weird_string
func treatKey(key string) string {
	spaces := regexp.MustCompile(`\s+`)
	key = spaces.ReplaceAllString(key, "_")
	return strings.ToLower(key)
}

func parseInt(from string) int64 {
	val, err := strconv.ParseInt(from, 10, 64)
	if err != nil {
		return 0
	}
	return val
}

func parseBgp(input string) Parsed {
	res := Parsed{}
	lines := getLinesFromString(input)
	route_changes := Parsed{}

	bgp_rx := regexp.MustCompile(`^([\w\.]+)\s+BGP\s+(\w+)\s+(\w+)\s+([0-9]{4}-[0-9]{2}-[0-9]{2}\s+[0-9]{2}:[0-9]{2}:[0-9]{2})\s*(\w+)?.*$`)
	num_val_rx := regexp.MustCompile(`^\s+([^:]+):\s+([\d]+)\s*$`)
	str_val_rx := regexp.MustCompile(`^\s+([^:]+):\s+(.+)\s*$`)
	routes_rx := regexp.MustCompile(`^\s+Routes:\s+(\d+)\s+imported,\s+(\d+)\s+filtered,\s+(\d+)\s+exported,\s+(\d+)\s+preferred\s*$`)
	imp_updates_rx := regexp.MustCompile(`^\s+Import updates:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$`)
	imp_withdraws_rx := regexp.MustCompile(`^\s+Import withdraws:\s+(\d+)\s+(\d+)\s+\-\-\-\s+(\d+)\s+(\d+)\s*$`)
	exp_updates_rx := regexp.MustCompile(`^\s+Export updates:\s+(\d+)\s+(\d+)\s+(\d+)\s+\-\-\-\s+(\d+)\s*$`)
	exp_withdraws_rx := regexp.MustCompile(`^\s+Export withdraws:\s+(\d+)(\s+\-\-\-){2}\s+(\d+)\s*$`)
	for _, line := range lines {
		if bgp_rx.MatchString(line) {
			groups := bgp_rx.FindStringSubmatch(line)

			res["protocol"] = groups[1]
			res["bird_protocol"] = "BGP"
			res["table"] = groups[2]
			res["state"] = groups[3]
			res["state_changed"] = groups[4]
			res["connection"] = groups[5]
		} else if routes_rx.MatchString(line) {
			routes := Parsed{}
			groups := routes_rx.FindStringSubmatch(line)

			routes["imported"] = parseInt(groups[1])
			routes["filtered"] = parseInt(groups[2])
			routes["exported"] = parseInt(groups[3])
			routes["preferred"] = parseInt(groups[4])

			res["routes"] = routes
		} else if imp_updates_rx.MatchString(line) {
			updates := Parsed{}
			groups := imp_updates_rx.FindStringSubmatch(line)

			updates["received"] = parseInt(groups[1])
			updates["rejected"] = parseInt(groups[2])
			updates["filtered"] = parseInt(groups[3])
			updates["ignored"] = parseInt(groups[4])
			updates["accepted"] = parseInt(groups[5])

			route_changes["import_updates"] = updates
		} else if imp_withdraws_rx.MatchString(line) {
			updates := Parsed{}
			groups := imp_withdraws_rx.FindStringSubmatch(line)

			updates["received"] = parseInt(groups[1])
			updates["rejected"] = parseInt(groups[2])
			updates["filtered"] = parseInt(groups[3])
			updates["accepted"] = parseInt(groups[4])

			route_changes["import_withdraws"] = updates
		} else if exp_updates_rx.MatchString(line) {
			updates := Parsed{}
			groups := exp_updates_rx.FindStringSubmatch(line)

			updates["received"] = parseInt(groups[1])
			updates["rejected"] = parseInt(groups[2])
			updates["ignored"] = parseInt(groups[3])
			updates["accepted"] = parseInt(groups[4])

			route_changes["export_updates"] = updates
		} else if exp_withdraws_rx.MatchString(line) {
			updates := Parsed{}
			groups := exp_withdraws_rx.FindStringSubmatch(line)

			updates["received"] = parseInt(groups[1])
			updates["accepted"] = parseInt(groups[3])

			route_changes["export_withdraws"] = updates
		} else if num_val_rx.MatchString(line) {
			groups := num_val_rx.FindStringSubmatch(line)

			key := treatKey(groups[1])

			res[key] = parseInt(groups[2])
		} else if str_val_rx.MatchString(line) {
			groups := str_val_rx.FindStringSubmatch(line)

			key := treatKey(groups[1])

			res[key] = groups[2]
		}
	}

	res["route_changes"] = route_changes

	if _, ok := res["routes"]; !ok {
		routes := Parsed{}

		routes["accepted"] = 0
		routes["filtered"] = 0
		routes["exported"] = 0
		routes["preferred"] = 0

		res["routes"] = routes
	}

	return res
}
<commit_msg>fixed regex, added as_path splitting<commit_after>package bird

import (
	"regexp"
	"strconv"
	"strings"
)

type Parsed map[string]interface{}

func emptyLine(line string) bool {
	return len(strings.TrimSpace(line)) == 0
}

func getLinesUnfiltered(input string) []string {
	line_sep := regexp.MustCompile(`((\r?\n)|(\r\n?))`)
	return line_sep.Split(input, -1)
}

func getLinesFromString(input string) []string {
	lines := getLinesUnfiltered(input)

	var filtered []string

	for _, line := range lines {
		if !emptyLine(line) {
			filtered = append(filtered, line)
		}
	}

	return filtered
}

func getLines(input []byte) []string {
	return getLinesFromString(string(input))
}

func specialLine(line string) bool {
	return (strings.HasPrefix(line, "BIRD") ||
		strings.HasPrefix(line, "Access restricted"))
}

func parseStatus(input []byte) Parsed {
	res := Parsed{}
	lines := getLines(input)

	start_line_rx := regexp.MustCompile(`^BIRD\s([0-9\.]+)\s*$`)
	router_id_rx := regexp.MustCompile(`^Router\sID\sis\s([0-9\.]+)\s*$`)
	current_server_rx := regexp.MustCompile(`^Current\sserver\stime\sis\s([0-9\-]+)\s([0-9\:]+)\s*$`)
	last_reboot_rx := regexp.MustCompile(`^Last\sreboot\son\s([0-9\-]+)\s([0-9\:]+)\s*$`)
	last_reconfig_rx := regexp.MustCompile(`^Last\sreconfiguration\son\s([0-9\-]+)\s([0-9\:]+)\s*$`)

	for _, line := range lines {
		if start_line_rx.MatchString(line) {
			res["version"] = start_line_rx.FindStringSubmatch(line)[1]
		} else if router_id_rx.MatchString(line) {
			res["router_id"] = router_id_rx.FindStringSubmatch(line)[1]
		} else if current_server_rx.MatchString(line) {
			res["current_server"] = current_server_rx.FindStringSubmatch(line)[1]
		} else if last_reboot_rx.MatchString(line) {
			res["last_reboot"] = last_reboot_rx.FindStringSubmatch(line)[1]
		} else if last_reconfig_rx.MatchString(line) {
			res["last_reconfig"] = last_reconfig_rx.FindStringSubmatch(line)[1]
		} else {
			res["message"] = line
		}
	}
	return Parsed{"status": res}
}

func parseProtocols(input []byte) Parsed {
	res := Parsed{}
	protocols := []string{}
	lines := getLinesUnfiltered(string(input))

	proto := ""
	for _, line := range lines {
		if emptyLine(line) {
			if !emptyLine(proto) {
				protocols = append(protocols, proto)
			}
			proto = ""
		} else {
			proto += (line + "\n")
		}
	}

	res["protocols"] = protocols
	return res
}

func parseSymbols(input []byte) Parsed {
	res := Parsed{}
	lines := getLines(input)

	key_rx := regexp.MustCompile(`^([^\s]+)\s+(.+)\s*$`)
	for _, line := range lines {
		if specialLine(line) {
			continue
		}

		if key_rx.MatchString(line) {
			groups := key_rx.FindStringSubmatch(line)
			res[groups[2]] = groups[1]
		}
	}

	return Parsed{"symbols": res}
}

func mainRouteDetail(groups []string, route Parsed) Parsed {
	route["network"] = groups[1]
	route["gateway"] = groups[2]
	route["interface"] = groups[3]
	route["from_protocol"] = groups[4]
	route["age"] = groups[5]
	route["learnt_from"] = groups[6]
	route["primary"] = groups[7] == "*"
	route["metric"] = parseInt(groups[8])
	return route
}

func parseRoutes(input []byte) Parsed {
	res := Parsed{}
	lines := getLines(input)

	routes := []Parsed{}

	route := Parsed{}
	start_def_rx := regexp.MustCompile(`^([0-9a-f\.\:\/]+)\s+via\s+([0-9a-f\.\:]+)\s+on\s+(\w+)\s+\[([\w\.]+)\s+([0-9\-\:\s]+)(?:\s+from\s+([0-9a-f\.\:\/]+)){0,1}\]\s+(?:(\*)\s+){0,1}\((\d+)(?:\/\d+){0,1}\).*`)
	second_rx := regexp.MustCompile(`^\s+via\s+([0-9a-f\.\:]+)\s+on\s+(\w+)\s+\[(\w+)\s+([0-9\-\:]+)(?:\s+from\s+([0-9a-f\.\:\/]+)){0,1}\]\s+(?:(\*)\s+){0,1}\((\d+)(?:\/\d+){0,1}\).*$`)
	type_rx := regexp.MustCompile(`^\s+Type:\s+(.*)\s*$`)
	bgp_rx := regexp.MustCompile(`^\s+BGP.(\w+):\s+(\w+)\s*$`)
	community_rx := regexp.MustCompile(`^\((\d+),(\d+)\)`)
	for _, line := range lines {
		if specialLine(line) || (len(route) == 0 && emptyLine(line)) {
			continue
		}

		if start_def_rx.MatchString(line) {
			if len(route) > 0 {
				routes = append(routes, route)
				route = Parsed{}
			}
			route = mainRouteDetail(start_def_rx.FindStringSubmatch(line), route)
		} else if second_rx.MatchString(line) {
			routes = append(routes, route)
			var network string
			if tmp, ok := route["network"]; ok {
				if val, ok := tmp.(string); ok {
					network = val
				} else {
					continue
				}
			} else {
				continue
			}
			route = Parsed{}

			groups := second_rx.FindStringSubmatch(line)
			first, groups := groups[0], groups[1:]
			groups = append([]string{network}, groups...)
			groups = append([]string{first}, groups...)
			route = mainRouteDetail(groups, route)
		} else if type_rx.MatchString(line) {
			submatch := type_rx.FindStringSubmatch(line)[1]
			route["type"] = strings.Split(submatch, " ")
		} else if bgp_rx.MatchString(line) {
			groups := bgp_rx.FindStringSubmatch(line)
			bgp := Parsed{}

			if tmp, ok := route["bgp"]; ok {
				if val, ok := tmp.(Parsed); ok {
					bgp = val
				}
			}

			if groups[1] == "community" {
				communities := [][]int64{}
				for _, community := range strings.Split(groups[2], " ") {
					if community_rx.MatchString(community) {
						com_groups := community_rx.FindStringSubmatch(community)
						maj := parseInt(com_groups[1])
						min := parseInt(com_groups[2])
						communities = append(communities, []int64{maj, min})
					}
				}
				bgp["communities"] = communities
			} else if groups[1] == "as_path" {
				bgp["as_path"] = strings.Split(groups[2], " ")
			} else {
				bgp[groups[1]] = groups[2]
			}

			route["bgp"] = bgp
		}
	}

	if len(route) > 0 {
		routes = append(routes, route)
	}

	res["routes"] = routes
	return res
}

func parseRoutesCount(input []byte) Parsed {
	res := Parsed{}
	lines := getLines(input)

	count_rx := regexp.MustCompile(`^(\d+)\s+of\s+(\d+)\s+routes.*$`)
	for _, line := range lines {
		if specialLine(line) {
			continue
		}

		if count_rx.MatchString(line) {
			count := count_rx.FindStringSubmatch(line)[1]
			res["routes"] = parseInt(count)
		}
	}

	return res
}

// Will snake_case a value like that:
// I am a Weird stRiNg -> i_am_a_weird_string
func treatKey(key string) string {
	spaces := regexp.MustCompile(`\s+`)
	key = spaces.ReplaceAllString(key, "_")
	return strings.ToLower(key)
}

func parseInt(from string) int64 {
	val, err := strconv.ParseInt(from, 10, 64)
	if err != nil {
		return 0
	}
	return val
}

func parseBgp(input string) Parsed {
	res := Parsed{}
	lines := getLinesFromString(input)
	route_changes := Parsed{}

	bgp_rx := regexp.MustCompile(`^([\w\.]+)\s+BGP\s+(\w+)\s+(\w+)\s+([0-9]{4}-[0-9]{2}-[0-9]{2}\s+[0-9]{2}:[0-9]{2}:[0-9]{2})\s*(\w+)?.*$`)
	num_val_rx := regexp.MustCompile(`^\s+([^:]+):\s+([\d]+)\s*$`)
	str_val_rx := regexp.MustCompile(`^\s+([^:]+):\s+(.+)\s*$`)
	routes_rx := regexp.MustCompile(`^\s+Routes:\s+(\d+)\s+imported,\s+(\d+)\s+filtered,\s+(\d+)\s+exported,\s+(\d+)\s+preferred\s*$`)
	imp_updates_rx := regexp.MustCompile(`^\s+Import updates:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$`)
	imp_withdraws_rx := regexp.MustCompile(`^\s+Import withdraws:\s+(\d+)\s+(\d+)\s+\-\-\-\s+(\d+)\s+(\d+)\s*$`)
	exp_updates_rx := regexp.MustCompile(`^\s+Export updates:\s+(\d+)\s+(\d+)\s+(\d+)\s+\-\-\-\s+(\d+)\s*$`)
	exp_withdraws_rx := regexp.MustCompile(`^\s+Export withdraws:\s+(\d+)(\s+\-\-\-){2}\s+(\d+)\s*$`)
	for _, line := range lines {
		if bgp_rx.MatchString(line) {
			groups := bgp_rx.FindStringSubmatch(line)

			res["protocol"] = groups[1]
			res["bird_protocol"] = "BGP"
			res["table"] = groups[2]
			res["state"] = groups[3]
			res["state_changed"] = groups[4]
			res["connection"] = groups[5]
		} else if routes_rx.MatchString(line) {
			routes := Parsed{}
			groups := routes_rx.FindStringSubmatch(line)

			routes["imported"] = parseInt(groups[1])
			routes["filtered"] = parseInt(groups[2])
			routes["exported"] = parseInt(groups[3])
			routes["preferred"] = parseInt(groups[4])

			res["routes"] = routes
		} else if imp_updates_rx.MatchString(line) {
			updates := Parsed{}
			groups := imp_updates_rx.FindStringSubmatch(line)

			updates["received"] = parseInt(groups[1])
			updates["rejected"] = parseInt(groups[2])
			updates["filtered"] = parseInt(groups[3])
			updates["ignored"] = parseInt(groups[4])
			updates["accepted"] = parseInt(groups[5])

			route_changes["import_updates"] = updates
		} else if imp_withdraws_rx.MatchString(line) {
			updates := Parsed{}
			groups := imp_withdraws_rx.FindStringSubmatch(line)

			updates["received"] = parseInt(groups[1])
			updates["rejected"] = parseInt(groups[2])
			updates["filtered"] = parseInt(groups[3])
			updates["accepted"] = parseInt(groups[4])

			route_changes["import_withdraws"] = updates
		} else if exp_updates_rx.MatchString(line) {
			updates := Parsed{}
			groups := exp_updates_rx.FindStringSubmatch(line)

			updates["received"] = parseInt(groups[1])
			updates["rejected"] = parseInt(groups[2])
			updates["ignored"] = parseInt(groups[3])
			updates["accepted"] = parseInt(groups[4])

			route_changes["export_updates"] = updates
		} else if exp_withdraws_rx.MatchString(line) {
			updates := Parsed{}
			groups := exp_withdraws_rx.FindStringSubmatch(line)

			updates["received"] = parseInt(groups[1])
			updates["accepted"] = parseInt(groups[3])

			route_changes["export_withdraws"] = updates
		} else if num_val_rx.MatchString(line) {
			groups := num_val_rx.FindStringSubmatch(line)

			key := treatKey(groups[1])

			res[key] = parseInt(groups[2])
		} else if str_val_rx.MatchString(line) {
			groups := str_val_rx.FindStringSubmatch(line)

			key := treatKey(groups[1])

			res[key] = groups[2]
		}
	}

	res["route_changes"] = route_changes

	if _, ok := res["routes"]; !ok {
		routes := Parsed{}

		routes["accepted"] = 0
		routes["filtered"] = 0
		routes["exported"] = 0
		routes["preferred"] = 0

		res["routes"] = routes
	}

	return res
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes 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 cinder

import (
	"errors"
	"io/ioutil"
	"strings"

	"github.com/golang/glog"
	"github.com/rackspace/gophercloud/openstack/blockstorage/v2/extensions/volumeactions"
)

type CinderBaremetalUtil struct {
	client             *cinderClient
	hostname           string
	isNoMountSupported bool
}

func (cb *CinderBaremetalUtil) AttachDiskBaremetal(b *cinderVolumeBuilder, globalPDPath string) error {
	volume, err := cb.client.getVolume(b.pdName)
	if err != nil {
		return err
	}

	glog.V(4).Infof("Begin to attach volume %v", volume)
	if len(volume.Attachments) > 0 {
		for _, att := range volume.Attachments {
			if att["host_name"].(string) == cb.hostname && att["device"].(string) == b.GetPath() {
				glog.V(5).Infof("Volume %s is already attached", b.pdName)
				return nil
			}
		}
	}

	if volume.Status != "available" {
		return errors.New("Volume is not available")
	}

	mountMode := "rw"
	if b.readOnly {
		mountMode = "ro"
	}

	// attach volume
	attachOpts := volumeactions.AttachOpts{
		MountPoint: b.GetPath(),
		Mode:       mountMode,
		HostName:   cb.hostname,
	}

	err = cb.client.attach(volume.ID, attachOpts)
	if err != nil && err.Error() != "EOF" {
		return err
	}

	connectionInfo, err := cb.client.getConnectionInfo(volume.ID, cb.getConnectionOptions())
	if err != nil {
		cb.client.detach(volume.ID)
		return err
	}

	volumeType := connectionInfo["driver_volume_type"].(string)
	data := connectionInfo["data"].(map[string]interface{})
	data["volume_type"] = volumeType
	if volumeType == "rbd" {
		data["keyring"] = cb.client.keyring
	}
	b.cinderVolume.metadata = data

	cinderDriver, err := GetCinderDriver(volumeType)
	if err != nil {
		glog.Warningf("Get cinder driver %s failed: %v", volumeType, err)
		cb.client.detach(volume.ID)
		return err
	}

	err = cinderDriver.Format(data, b.fsType)
	if err != nil {
		glog.Warningf("Format cinder volume %s failed: %v", b.pdName, err)
		cb.client.detach(volume.ID)
		return err
	}

	if cb.isNoMountSupported && volumeType == "rbd" {
		glog.V(4).Infof("Volume %s willn't be mounted on host since rbd is natively supported",
			volume.Name)
	} else {
		err = cinderDriver.Attach(data, globalPDPath)
		if err != nil {
			cb.client.detach(volume.ID)
			return err
		}
	}

	return nil
}

// Unmounts the device and detaches the disk from the kubelet's host machine.
func (cb *CinderBaremetalUtil) DetachDiskBaremetal(cd *cinderVolumeCleaner, globalPDPath string) error {
	volume, err := cb.client.getVolume(cd.pdName)
	if err != nil {
		return err
	}

	connectionInfo, err := cb.client.getConnectionInfo(volume.ID, cb.getConnectionOptions())
	if err != nil {
		return err
	}

	volumeType := connectionInfo["driver_volume_type"].(string)
	cinderDriver, err := GetCinderDriver(volumeType)
	if err != nil {
		glog.Warningf("Get cinder driver %s failed: %v", volumeType, err)
		return err
	}
	data := connectionInfo["data"].(map[string]interface{})
	if volumeType == "rbd" {
		data["keyring"] = cb.client.keyring
	}

	if cb.isNoMountSupported && volumeType == "rbd" {
		glog.V(4).Infof("Volume %s is not mounted since rbd is natively supported", volume.Name)
	} else {
		err = cinderDriver.Detach(data, globalPDPath)
		if err != nil {
			return err
		}
	}

	err = cb.client.terminateConnection(volume.ID, cb.getConnectionOptions())
	if err != nil {
		return err
	}

	if volume.Status == "available" {
		return nil
	}

	err = cb.client.detach(volume.ID)
	if err != nil {
		return err
	}

	return nil
}

// Get iscsi initiator
func (cb *CinderBaremetalUtil) getIscsiInitiator() string {
	contents, err := ioutil.ReadFile("/etc/iscsi/initiatorname.iscsi")
	if err != nil {
		return ""
	}

	lines := strings.Split(string(contents), "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "InitiatorName=") {
			return strings.Split(line, "=")[1]
		}
	}

	return ""
}

// Get cinder connections options
func (cb *CinderBaremetalUtil) getConnectionOptions() *volumeactions.ConnectorOpts {
	connector := volumeactions.ConnectorOpts{
		Host:      cb.hostname,
		Initiator: cb.getIscsiInitiator(),
	}

	return &connector
}
<commit_msg>Check cinder volume attach status and setup volume metadata<commit_after>/*
Copyright 2015 The Kubernetes 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 cinder

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/golang/glog"
	"github.com/rackspace/gophercloud/openstack/blockstorage/v2/extensions/volumeactions"
)

type CinderBaremetalUtil struct {
	client             *cinderClient
	hostname           string
	isNoMountSupported bool
}

func (cb *CinderBaremetalUtil) AttachDiskBaremetal(b *cinderVolumeBuilder, globalPDPath string) error {
	glog.V(4).Infof("Begin to attach volume %v", b.pdName)
	volume, err := cb.client.getVolume(b.pdName)
	if err != nil {
		glog.Errorf("Get volume %s error: %v", b.pdName, err)
		return err
	}

	var attached bool
	if len(volume.Attachments) > 0 || volume.Status != "available" {
		for _, att := range volume.Attachments {
			if att["host_name"].(string) == cb.hostname && att["device"].(string) == b.GetPath() {
				glog.V(5).Infof("Volume %s is already attached", b.pdName)
				attached = true
				break
			}
		}

		if !attached {
			return fmt.Errorf("Volume %s is not available", b.pdName)
		}
	}

	connectionInfo, err := cb.client.getConnectionInfo(volume.ID, cb.getConnectionOptions())
	if err != nil {
		return err
	}

	volumeType := connectionInfo["driver_volume_type"].(string)
	data := connectionInfo["data"].(map[string]interface{})
	data["volume_type"] = volumeType
	if volumeType == "rbd" {
		data["keyring"] = cb.client.keyring
	}
	b.cinderVolume.metadata = data

	// already attached, just return
	if attached {
		return nil
	}

	mountMode := "rw"
	if b.readOnly {
		mountMode = "ro"
	}

	// attach volume
	attachOpts := volumeactions.AttachOpts{
		MountPoint: b.GetPath(),
		Mode:       mountMode,
		HostName:   cb.hostname,
	}

	err = cb.client.attach(volume.ID, attachOpts)
	if err != nil && err.Error() != "EOF" {
		return err
	}

	cinderDriver, err := GetCinderDriver(volumeType)
	if err != nil {
		glog.Warningf("Get cinder driver %s failed: %v", volumeType, err)
		cb.client.detach(volume.ID)
		return err
	}

	err = cinderDriver.Format(data, b.fsType)
	if err != nil {
		glog.Warningf("Format cinder volume %s failed: %v", b.pdName, err)
		cb.client.detach(volume.ID)
		return err
	}

	if cb.isNoMountSupported && volumeType == "rbd" {
		glog.V(4).Infof("Volume %s willn't be mounted on host since rbd is natively supported",
			volume.Name)
	} else {
		err = cinderDriver.Attach(data, globalPDPath)
		if err != nil {
			cb.client.detach(volume.ID)
			return err
		}
	}

	return nil
}

// Unmounts the device and detaches the disk from the kubelet's host machine.
func (cb *CinderBaremetalUtil) DetachDiskBaremetal(cd *cinderVolumeCleaner, globalPDPath string) error {
	volume, err := cb.client.getVolume(cd.pdName)
	if err != nil {
		return err
	}

	connectionInfo, err := cb.client.getConnectionInfo(volume.ID, cb.getConnectionOptions())
	if err != nil {
		return err
	}

	volumeType := connectionInfo["driver_volume_type"].(string)
	cinderDriver, err := GetCinderDriver(volumeType)
	if err != nil {
		glog.Warningf("Get cinder driver %s failed: %v", volumeType, err)
		return err
	}
	data := connectionInfo["data"].(map[string]interface{})
	if volumeType == "rbd" {
		data["keyring"] = cb.client.keyring
	}

	if cb.isNoMountSupported && volumeType == "rbd" {
		glog.V(4).Infof("Volume %s is not mounted since rbd is natively supported", volume.Name)
	} else {
		err = cinderDriver.Detach(data, globalPDPath)
		if err != nil {
			return err
		}
	}

	err = cb.client.terminateConnection(volume.ID, cb.getConnectionOptions())
	if err != nil {
		return err
	}

	if volume.Status == "available" {
		return nil
	}

	err = cb.client.detach(volume.ID)
	if err != nil {
		return err
	}

	return nil
}

// Get iscsi initiator
func (cb *CinderBaremetalUtil) getIscsiInitiator() string {
	contents, err := ioutil.ReadFile("/etc/iscsi/initiatorname.iscsi")
	if err != nil {
		return ""
	}

	lines := strings.Split(string(contents), "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "InitiatorName=") {
			return strings.Split(line, "=")[1]
		}
	}

	return ""
}

// Get cinder connections options
func (cb *CinderBaremetalUtil) getConnectionOptions() *volumeactions.ConnectorOpts {
	connector := volumeactions.ConnectorOpts{
		Host:      cb.hostname,
		Initiator: cb.getIscsiInitiator(),
	}

	return &connector
}
<|endoftext|>


<commit_before>package scraper

import (
	"io"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/uber-go/zap"
)

func (s *Scraper) fixFileReferences(URL *url.URL, buf io.Reader) (string, error) {
	var relativeToRoot string
	splits := strings.Split(URL.Path, "/")
	for i := range splits {
		if len(splits[i]) > 0 {
			relativeToRoot += "../"
		}
	}

	g, err := goquery.NewDocumentFromReader(buf)
	if err != nil {
		return "", err
	}

	g.Find("a").Each(func(_ int, selection *goquery.Selection) {
		s.fixQuerySelection(URL, "href", selection, true, relativeToRoot)
	})

	g.Find("link").Each(func(_ int, selection *goquery.Selection) {
		s.fixQuerySelection(URL, "href", selection, false, relativeToRoot)
	})

	g.Find("img").Each(func(_ int, selection *goquery.Selection) {
		s.fixQuerySelection(URL, "src", selection, false, relativeToRoot)
	})

	g.Find("script").Each(func(_ int, selection *goquery.Selection) {
		s.fixQuerySelection(URL, "src", selection, false, relativeToRoot)
	})

	return g.Html()
}

func (s *Scraper) fixQuerySelection(URL *url.URL, attribute string, selection *goquery.Selection, linkIsAPage bool, relativeToRoot string) {
	src, ok := selection.Attr(attribute)
	if !ok {
		return
	}

	ur, err := url.Parse(src)
	if err != nil {
		return
	}

	var refRes *url.URL
	if ur.Host != "" && ur.Host != s.URL.Host {
		refRes = URL.ResolveReference(ur)
		refRes.Path = filepath.Join("_"+ur.Host, refRes.Path)
	} else {
		refRes = URL.ResolveReference(ur)
	}

	refRes.Host = ""   // remove host
	refRes.Scheme = "" // remove http/https
	refStr := refRes.String()

	if refStr == "" {
		refStr = "/" // website root
	} else {
		if refStr[0] == '/' && len(relativeToRoot) > 0 {
			refStr = relativeToRoot + refStr[1:]
		} else {
			refStr = relativeToRoot + refStr
		}
	}

	if linkIsAPage && refStr[len(refStr)-1] == '/' {
		refStr += "index.html"
	}
	refStr = strings.TrimPrefix(refStr, "/")

	if src == refStr { // nothing changed
		return
	}

	s.log.Debug("HTML Element relinked", zap.String("URL", src), zap.String("Fixed", refStr))
	selection.SetAttr(attribute, refStr)
}
<commit_msg>Relinking to anchors and external websites fixed<commit_after>package scraper

import (
	"io"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/uber-go/zap"
)

func (s *Scraper) fixFileReferences(URL *url.URL, buf io.Reader) (string, error) {
	var relativeToRoot string
	splits := strings.Split(URL.Path, "/")
	for i := range splits {
		if len(splits[i]) > 0 {
			relativeToRoot += "../"
		}
	}

	g, err := goquery.NewDocumentFromReader(buf)
	if err != nil {
		return "", err
	}

	g.Find("a").Each(func(_ int, selection *goquery.Selection) {
		s.fixQuerySelection(URL, "href", selection, true, relativeToRoot)
	})

	g.Find("link").Each(func(_ int, selection *goquery.Selection) {
		s.fixQuerySelection(URL, "href", selection, false, relativeToRoot)
	})

	g.Find("img").Each(func(_ int, selection *goquery.Selection) {
		s.fixQuerySelection(URL, "src", selection, false, relativeToRoot)
	})

	g.Find("script").Each(func(_ int, selection *goquery.Selection) {
		s.fixQuerySelection(URL, "src", selection, false, relativeToRoot)
	})

	return g.Html()
}

func (s *Scraper) fixQuerySelection(URL *url.URL, attribute string, selection *goquery.Selection, linkIsAPage bool, relativeToRoot string) {
	src, ok := selection.Attr(attribute)
	if !ok {
		return
	}

	ur, err := url.Parse(src)
	if err != nil {
		return
	}

	var refRes *url.URL
	if ur.Host != "" && ur.Host != s.URL.Host {
		if linkIsAPage { // do not change links to external websites
			return
		}

		refRes = URL.ResolveReference(ur)
		refRes.Path = filepath.Join("_"+ur.Host, refRes.Path)
	} else {
		refRes = URL.ResolveReference(ur)
	}

	refRes.Host = ""   // remove host
	refRes.Scheme = "" // remove http/https
	refStr := refRes.String()

	if refStr == "" {
		refStr = "/" // website root
	} else {
		if refStr[0] == '/' && len(relativeToRoot) > 0 {
			refStr = relativeToRoot + refStr[1:]
		} else {
			refStr = relativeToRoot + refStr
		}
	}

	if linkIsAPage {
		if refStr[len(refStr)-1] == '/' {
			refStr += "index.html" // link dir index to index.html
		} else {
			l := strings.LastIndexByte(refStr, '/')
			if l != -1 && l < len(refStr) && refStr[l+1] == '#' {
				refStr = refStr[:l+1] + "index.html" + refStr[l+1:] // link anchor correct
			}
		}
	}

	refStr = strings.TrimPrefix(refStr, "/")

	if src == refStr { // nothing changed
		return
	}

	s.log.Debug("HTML Element relinked", zap.String("URL", src), zap.String("Fixed", refStr))
	selection.SetAttr(attribute, refStr)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	BuildOS   = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll  = flag.Bool("all", false, "Builds all architectures")
	ShowHelp  = flag.Bool("help", false, "Shows help")
)

func main() {
	cmd, err := exec.Command("script/fmt").Output()
	if err != nil {
		panic(err)
	}

	if len(cmd) > 0 {
		fmt.Println(string(cmd))
	}

	flag.Parse()
	if *ShowHelp {
		fmt.Println("usage: script/build [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				build(buildos, buildarch)
			}
		}
	} else {
		build(*BuildOS, *BuildArch)
	}
}

func build(buildos, buildarch string) {
	if len(buildos) > 0 && len(buildarch) > 0 {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
	}

	filepath.Walk("cmd", func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(path, ".go") {
			return nil
		}

		return buildCommand(path, buildos, buildarch)
	})
}

func buildCommand(path, buildos, buildarch string) error {
	base := filepath.Base(path)
	base = base[0 : len(base)-3]
	bin := "bin"
	addenv := len(buildos) > 0 && len(buildarch) > 0

	if addenv {
		bin = filepath.Join(bin, buildos+"-"+buildarch, base)
	} else {
		bin = filepath.Join(bin, base)
	}

	cmd := exec.Command("go", "build", "-o", bin, path)
	var out bytes.Buffer
	cmd.Stderr = &out
	if addenv {
		cmd.Env = []string{"GOOS=" + buildos, "GOARCH=" + buildarch, "GOPATH=" + os.Getenv("GOPATH")}
	}

	if err := cmd.Run(); err != nil {
		fmt.Println(out.String())
		return err
	}

	return nil
}
<commit_msg>ラララララ ラー ウウウ フフフ<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	BuildOS   = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll  = flag.Bool("all", false, "Builds all architectures")
	ShowHelp  = flag.Bool("help", false, "Shows help")
)

func main() {
	cmd, err := exec.Command("script/fmt").Output()
	if err != nil {
		panic(err)
	}

	if len(cmd) > 0 {
		fmt.Println(string(cmd))
	}

	flag.Parse()
	if *ShowHelp {
		fmt.Println("usage: script/build [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				build(buildos, buildarch)
			}
		}
	} else {
		build(*BuildOS, *BuildArch)
	}
}

func build(buildos, buildarch string) {
	if len(buildos) > 0 && len(buildarch) > 0 {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
	}

	filepath.Walk("cmd", func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(path, ".go") {
			return nil
		}

		return buildCommand(path, buildos, buildarch)
	})
}

func buildCommand(path, buildos, buildarch string) error {
	base := filepath.Base(path)
	base = base[0 : len(base)-3]
	dir := "bin"
	addenv := len(buildos) > 0 && len(buildarch) > 0

	if addenv {
		dir = filepath.Join(dir, buildos+"-"+buildarch)
	}

	bin := filepath.Join(dir, base)

	cmd := exec.Command("go", "build", "-o", bin, path)
	var out bytes.Buffer
	cmd.Stderr = &out
	if addenv {
		cmd.Env = []string{"GOOS=" + buildos, "GOARCH=" + buildarch, "GOPATH=" + os.Getenv("GOPATH")}
	}

	if err := cmd.Run(); err != nil {
		fmt.Println(out.String())
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright Project Harbor 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 v2auth

import (
	"errors"
	"fmt"
	serror "github.com/goharbor/harbor/src/server/error"
	"net/http"
	"sync"

	"github.com/goharbor/harbor/src/common/rbac"
	"github.com/goharbor/harbor/src/common/security"
	"github.com/goharbor/harbor/src/common/utils/log"
	"github.com/goharbor/harbor/src/core/config"
	"github.com/goharbor/harbor/src/core/promgr"
	ierror "github.com/goharbor/harbor/src/internal/error"
	"github.com/goharbor/harbor/src/server/middleware"
)

type reqChecker struct {
	pm promgr.ProjectManager
}

func (rc *reqChecker) check(req *http.Request) error {
	if rc.hasRegistryCred(req) {
		// TODO: May consider implement a local authorizer for registry, more details see #10602
		return nil
	}
	securityCtx, ok := security.FromContext(req.Context())
	if !ok {
		return fmt.Errorf("the security context got from request is nil")
	}
	if a, ok := middleware.ArtifactInfoFromContext(req.Context()); ok {
		action := getAction(req)
		if action == "" {
			return nil
		}
		log.Debugf("action: %s, repository: %s", action, a.Repository)
		pid, err := rc.projectID(a.ProjectName)
		if err != nil {
			return err
		}
		resource := rbac.NewProjectNamespace(pid).Resource(rbac.ResourceRepository)
		if !securityCtx.Can(action, resource) {
			return fmt.Errorf("unauthorized to access repository: %s, action: %s", a.Repository, action)
		}
		if req.Method == http.MethodPost && a.BlobMountProjectName != "" { // check permission for the source of blob mount
			pid, err := rc.projectID(a.BlobMountProjectName)
			if err != nil {
				return err
			}
			resource := rbac.NewProjectNamespace(pid).Resource(rbac.ResourceRepository)
			if !securityCtx.Can(rbac.ActionPull, resource) {
				return fmt.Errorf("unauthorized to access repository from which to mount blob: %s, action: %s", a.BlobMountRepository, rbac.ActionPull)
			}
		}
	} else if len(middleware.V2CatalogURLRe.FindStringSubmatch(req.URL.Path)) == 1 && !securityCtx.IsSysAdmin() {
		return fmt.Errorf("unauthorized to list catalog")
	} else if req.URL.Path == "/v2/" && !securityCtx.IsAuthenticated() {
		return errors.New("unauthorized")
	}
	return nil
}

func (rc *reqChecker) projectID(name string) (int64, error) {
	p, err := rc.pm.Get(name)
	if err != nil {
		return 0, err
	}
	if p == nil {
		return 0, fmt.Errorf("project not found, name: %s", name)
	}
	return p.ProjectID, nil
}

func (rc *reqChecker) hasRegistryCred(req *http.Request) bool {
	u, p, ok := req.BasicAuth()
	regUser, regPass := config.RegistryCredential()
	return ok && u == regUser && p == regPass
}

func getAction(req *http.Request) rbac.Action {
	pushActions := map[string]struct{}{
		http.MethodPost:   {},
		http.MethodDelete: {},
		http.MethodPatch:  {},
		http.MethodPut:    {},
	}
	pullActions := map[string]struct{}{
		http.MethodGet:  {},
		http.MethodHead: {},
	}
	if _, ok := pushActions[req.Method]; ok {
		return rbac.ActionPush
	}
	if _, ok := pullActions[req.Method]; ok {
		return rbac.ActionPull
	}
	return ""

}

var (
	once    sync.Once
	checker reqChecker
)

// Middleware checks the permission of the request to access the artifact
func Middleware() func(http.Handler) http.Handler {
	once.Do(func() {
		if checker.pm == nil { // for UT, where pm has been set to a mock value
			checker = reqChecker{
				pm: config.GlobalProjectMgr,
			}
		}
	})
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			if err := checker.check(req); err != nil {
				serror.SendError(rw, ierror.UnauthorizedError(err).WithMessage(err.Error()))
				return
			}
			next.ServeHTTP(rw, req)
		})
	}
}
<commit_msg>Add "Docker-Distribution-Api-Version" header for the 401 response of registry API<commit_after>// Copyright Project Harbor 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 v2auth

import (
	"errors"
	"fmt"
	serror "github.com/goharbor/harbor/src/server/error"
	"net/http"
	"sync"

	"github.com/goharbor/harbor/src/common/rbac"
	"github.com/goharbor/harbor/src/common/security"
	"github.com/goharbor/harbor/src/common/utils/log"
	"github.com/goharbor/harbor/src/core/config"
	"github.com/goharbor/harbor/src/core/promgr"
	ierror "github.com/goharbor/harbor/src/internal/error"
	"github.com/goharbor/harbor/src/server/middleware"
)

type reqChecker struct {
	pm promgr.ProjectManager
}

func (rc *reqChecker) check(req *http.Request) error {
	if rc.hasRegistryCred(req) {
		// TODO: May consider implement a local authorizer for registry, more details see #10602
		return nil
	}
	securityCtx, ok := security.FromContext(req.Context())
	if !ok {
		return fmt.Errorf("the security context got from request is nil")
	}
	if a, ok := middleware.ArtifactInfoFromContext(req.Context()); ok {
		action := getAction(req)
		if action == "" {
			return nil
		}
		log.Debugf("action: %s, repository: %s", action, a.Repository)
		pid, err := rc.projectID(a.ProjectName)
		if err != nil {
			return err
		}
		resource := rbac.NewProjectNamespace(pid).Resource(rbac.ResourceRepository)
		if !securityCtx.Can(action, resource) {
			return fmt.Errorf("unauthorized to access repository: %s, action: %s", a.Repository, action)
		}
		if req.Method == http.MethodPost && a.BlobMountProjectName != "" { // check permission for the source of blob mount
			pid, err := rc.projectID(a.BlobMountProjectName)
			if err != nil {
				return err
			}
			resource := rbac.NewProjectNamespace(pid).Resource(rbac.ResourceRepository)
			if !securityCtx.Can(rbac.ActionPull, resource) {
				return fmt.Errorf("unauthorized to access repository from which to mount blob: %s, action: %s", a.BlobMountRepository, rbac.ActionPull)
			}
		}
	} else if len(middleware.V2CatalogURLRe.FindStringSubmatch(req.URL.Path)) == 1 && !securityCtx.IsSysAdmin() {
		return fmt.Errorf("unauthorized to list catalog")
	} else if req.URL.Path == "/v2/" && !securityCtx.IsAuthenticated() {
		return errors.New("unauthorized")
	}
	return nil
}

func (rc *reqChecker) projectID(name string) (int64, error) {
	p, err := rc.pm.Get(name)
	if err != nil {
		return 0, err
	}
	if p == nil {
		return 0, fmt.Errorf("project not found, name: %s", name)
	}
	return p.ProjectID, nil
}

func (rc *reqChecker) hasRegistryCred(req *http.Request) bool {
	u, p, ok := req.BasicAuth()
	regUser, regPass := config.RegistryCredential()
	return ok && u == regUser && p == regPass
}

func getAction(req *http.Request) rbac.Action {
	pushActions := map[string]struct{}{
		http.MethodPost:   {},
		http.MethodDelete: {},
		http.MethodPatch:  {},
		http.MethodPut:    {},
	}
	pullActions := map[string]struct{}{
		http.MethodGet:  {},
		http.MethodHead: {},
	}
	if _, ok := pushActions[req.Method]; ok {
		return rbac.ActionPush
	}
	if _, ok := pullActions[req.Method]; ok {
		return rbac.ActionPull
	}
	return ""

}

var (
	once    sync.Once
	checker reqChecker
)

// Middleware checks the permission of the request to access the artifact
func Middleware() func(http.Handler) http.Handler {
	once.Do(func() {
		if checker.pm == nil { // for UT, where pm has been set to a mock value
			checker = reqChecker{
				pm: config.GlobalProjectMgr,
			}
		}
	})
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			if err := checker.check(req); err != nil {
				// the header is needed for "docker manifest" commands: https://github.com/docker/cli/issues/989
				rw.Header().Set("Docker-Distribution-Api-Version", "registry/2.0")
				serror.SendError(rw, ierror.UnauthorizedError(err).WithMessage(err.Error()))
				return
			}
			next.ServeHTTP(rw, req)
		})
	}
}
<|endoftext|>


<commit_before>package core

import (
	"os"
	"sync"
	"syscall"

	"github.com/elves/elvish/edit/tty"
	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/newedit/loop"
	"github.com/elves/elvish/styled"
	"github.com/elves/elvish/sys"
)

type Editor struct {
	// Dependencies
	loop *loop.Loop
	tty  TTY
	sigs SignalSource

	Config *Config
	State  *State

	// Internal states
	prompt, rprompt styled.Text
}

func NewEditor(t TTY, sigs SignalSource) *Editor {
	lp := loop.New()
	ed := &Editor{
		lp, t, sigs, &Config{}, &State{}, nil, nil,
	}
	lp.HandleCb(ed.handle)
	lp.RedrawCb(ed.redraw)
	return ed
}

func (ed *Editor) handle(e loop.Event) (string, bool) {
	switch e := e.(type) {
	case os.Signal:
		switch e {
		case syscall.SIGHUP:
			return "", true
		case syscall.SIGINT:
			ed.State.Reset()
			ed.triggerPrompts()
		case sys.SIGWINCH:
			ed.Redraw(true)
		}
		return "", false
	case tty.Event:
		switch e := e.(type) {
		case tty.KeyEvent:
			action := ed.State.Mode().HandleKey(ui.Key(e), ed.State)

			switch action {
			case CommitCode:
				return ed.State.Code(), true
			}
			ed.triggerPrompts()
		}
		return "", false
	default:
		panic("unreachable")
	}
}

func (ed *Editor) triggerPrompts() {
	ed.Config.triggerPrompts()
}

func (ed *Editor) redraw(flag loop.RedrawFlag) {
	redraw(ed.State, ed.Config, ed.tty, ed.tty, flag)
}

func redraw(s *State, cfg *Config, w Output, sz Sizer, flag loop.RedrawFlag) {
	var rawState *RawState
	final := flag&loop.FinalRedraw != 0
	if final {
		rawState = s.finalize()
	} else {
		rawState = s.CopyRaw()
	}

	height, width := sz.Size()

	bufNotes, bufMain := render(rawState, makeRenderSetup(cfg, height, width))

	w.UpdateBuffer(bufNotes, bufMain, flag&loop.FullRedraw != 0)

	if final {
		w.Newline()
		w.ResetBuffer()
	}
}

func (ed *Editor) ReadCode() (string, error) {
	restore, err := ed.tty.Setup()
	if err != nil {
		return "", err
	}
	defer restore()

	var wg sync.WaitGroup
	defer wg.Wait()

	// Relay input events.
	eventCh := ed.tty.StartInput()
	defer ed.tty.StopInput()
	wg.Add(1)
	go func() {
		for event := range eventCh {
			ed.loop.Input(event)
		}
		wg.Done()
	}()

	if ed.sigs != nil {
		// Relay signals.
		sigCh := ed.sigs.NotifySignals()
		defer ed.sigs.StopSignals()
		wg.Add(1)
		go func() {
			for sig := range sigCh {
				ed.loop.Input(sig)
			}
			wg.Done()
		}()
	}

	// TODO: relay late prompt/rprompt updates.

	// Reset state before returning.
	defer ed.State.Reset()

	// BeforeReadline and AfterReadline hooks.
	for _, f := range ed.Config.BeforeReadline() {
		f()
	}
	defer func() {
		code := ed.State.Code()
		for _, f := range ed.Config.AfterReadline() {
			f(code)
		}
	}()

	return ed.loop.Run()
}

// Like ReadCode, but returns immediately with two channels that will get the
// return values of ReadCode. Useful in tests.
func (ed *Editor) readCodeAsync() (<-chan string, <-chan error) {
	codeCh := make(chan string, 1)
	errCh := make(chan error, 1)
	go func() {
		code, err := ed.ReadCode()
		codeCh <- code
		errCh <- err
	}()
	return codeCh, errCh
}

func (ed *Editor) Redraw(full bool) {
	ed.loop.Redraw(full)
}
<commit_msg>newedit/core: Remove unneeded fields from Editor.<commit_after>package core

import (
	"os"
	"sync"
	"syscall"

	"github.com/elves/elvish/edit/tty"
	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/newedit/loop"
	"github.com/elves/elvish/sys"
)

type Editor struct {
	// Dependencies
	loop *loop.Loop
	tty  TTY
	sigs SignalSource

	Config *Config
	State  *State
}

func NewEditor(t TTY, sigs SignalSource) *Editor {
	lp := loop.New()
	ed := &Editor{
		lp, t, sigs, &Config{}, &State{},
	}
	lp.HandleCb(ed.handle)
	lp.RedrawCb(ed.redraw)
	return ed
}

func (ed *Editor) handle(e loop.Event) (string, bool) {
	switch e := e.(type) {
	case os.Signal:
		switch e {
		case syscall.SIGHUP:
			return "", true
		case syscall.SIGINT:
			ed.State.Reset()
			ed.triggerPrompts()
		case sys.SIGWINCH:
			ed.Redraw(true)
		}
		return "", false
	case tty.Event:
		switch e := e.(type) {
		case tty.KeyEvent:
			action := ed.State.Mode().HandleKey(ui.Key(e), ed.State)

			switch action {
			case CommitCode:
				return ed.State.Code(), true
			}
			ed.triggerPrompts()
		}
		return "", false
	default:
		panic("unreachable")
	}
}

func (ed *Editor) triggerPrompts() {
	ed.Config.triggerPrompts()
}

func (ed *Editor) redraw(flag loop.RedrawFlag) {
	redraw(ed.State, ed.Config, ed.tty, ed.tty, flag)
}

func redraw(s *State, cfg *Config, w Output, sz Sizer, flag loop.RedrawFlag) {
	var rawState *RawState
	final := flag&loop.FinalRedraw != 0
	if final {
		rawState = s.finalize()
	} else {
		rawState = s.CopyRaw()
	}

	height, width := sz.Size()

	bufNotes, bufMain := render(rawState, makeRenderSetup(cfg, height, width))

	w.UpdateBuffer(bufNotes, bufMain, flag&loop.FullRedraw != 0)

	if final {
		w.Newline()
		w.ResetBuffer()
	}
}

func (ed *Editor) ReadCode() (string, error) {
	restore, err := ed.tty.Setup()
	if err != nil {
		return "", err
	}
	defer restore()

	var wg sync.WaitGroup
	defer wg.Wait()

	// Relay input events.
	eventCh := ed.tty.StartInput()
	defer ed.tty.StopInput()
	wg.Add(1)
	go func() {
		for event := range eventCh {
			ed.loop.Input(event)
		}
		wg.Done()
	}()

	if ed.sigs != nil {
		// Relay signals.
		sigCh := ed.sigs.NotifySignals()
		defer ed.sigs.StopSignals()
		wg.Add(1)
		go func() {
			for sig := range sigCh {
				ed.loop.Input(sig)
			}
			wg.Done()
		}()
	}

	// TODO: relay late prompt/rprompt updates.

	// Reset state before returning.
	defer ed.State.Reset()

	// BeforeReadline and AfterReadline hooks.
	for _, f := range ed.Config.BeforeReadline() {
		f()
	}
	defer func() {
		code := ed.State.Code()
		for _, f := range ed.Config.AfterReadline() {
			f(code)
		}
	}()

	return ed.loop.Run()
}

// Like ReadCode, but returns immediately with two channels that will get the
// return values of ReadCode. Useful in tests.
func (ed *Editor) readCodeAsync() (<-chan string, <-chan error) {
	codeCh := make(chan string, 1)
	errCh := make(chan error, 1)
	go func() {
		code, err := ed.ReadCode()
		codeCh <- code
		errCh <- err
	}()
	return codeCh, errCh
}

func (ed *Editor) Redraw(full bool) {
	ed.loop.Redraw(full)
}
<|endoftext|>


<commit_before>package broker

import (
	zmq "github.com/pebbe/zmq4"
	"log"
	"time"
)

func (b *Broker) Poll(messageHandler *MessageHandler) {
	b.Connect()
	defer b.Close()

	poller := zmq.NewPoller()
	poller.Add(b.socket, zmq.POLLIN)

	for {
		polled, err := poller.Poll(time.Second * 10)

		if err != nil {
			log.Println("E: Interrupted")
			log.Printf("%q\n", err)
			break //  Interrupted
		}

		if len(polled) <= 0 {
			continue
		}

		msg, err := b.socket.RecvMessage(1)

		if err != nil {
			log.Println("E: Interrupted")
			log.Printf("%q\n", err)
			break //  Interrupted
		}

		message, err := NewMessage(msg)

		if err != nil {
			log.Println("!: Message malformed")
			continue
		}

		response, err := messageHandler.Respond(message)

		if err != nil {
			log.Println("!: Could not respond")
			continue
		}

		if len(response) == 0 {
			log.Println("!: Will not respond")
			continue
		}

		msgCount, err := b.socket.SendMessage(response)

		if err != nil {
			log.Printf("! %x\n", err)
		} else {
			log.Printf("I: sent %i bytes\n", msgCount)
		}
	}
}
<commit_msg>Ensure socket closes when runtime exits<commit_after>package broker

import (
	zmq "github.com/pebbe/zmq4"
	"log"
	"runtime"
	"time"
)

func (b *Broker) Poll(messageHandler *MessageHandler) {
	b.Connect()
	runtime.SetFinalizer(b, (*Broker).Close)

	poller := zmq.NewPoller()
	poller.Add(b.socket, zmq.POLLIN)

	for {
		polled, err := poller.Poll(time.Second * 10)

		if err != nil {
			log.Println("E: Interrupted")
			log.Printf("%q\n", err)
			break //  Interrupted
		}

		if len(polled) <= 0 {
			// TODO: heartbeat
			continue
		}

		msg, err := b.socket.RecvMessage(0)

		if err != nil {
			log.Println("E: Interrupted")
			log.Printf("%q\n", err)
			break //  Interrupted
		}

		message, err := NewMessage(msg)

		if err != nil {
			log.Println("!: Message malformed")
			continue
		}

		response, err := messageHandler.Respond(message)

		if err != nil {
			log.Println("!: Could not respond")
			continue
		}

		if len(response) == 0 {
			log.Println("!: Will not respond")
			continue
		}

		msgCount, err := b.socket.SendMessage(response)

		if err != nil {
			log.Printf("! %x\n", err)
		} else {
			log.Printf("I: sent %i bytes\n", msgCount)
		}
	}
}
<|endoftext|>