<commit_before>package pod

import (
	"fmt"
	"math/rand"

	"github.com/miekg/dns"
	"github.com/openshift/origin/pkg/diagnostics/types"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	PodCheckDnsName = "PodCheckDns"
	txtDP2016       = `
Query to nameserver %s for random subdomain of %s
returned an answer, indicating that this domain provides wildcard DNS.

This domain should not be in the search.

It can cause queries for external domains (e.g. github.com)
to resolve to the wildcard address. Full response:

%v`
)

// PodCheckDns is a Diagnostic to check that DNS within a pod works as expected
type PodCheckDns struct {
}

// Name is part of the Diagnostic interface and just returns name.
func (d PodCheckDns) Name() string {
	return PodCheckDnsName
}

// Description is part of the Diagnostic interface and just returns the diagnostic description.
func (d PodCheckDns) Description() string {
	return "Check that DNS within a pod works as expected"
}

// CanRun is part of the Diagnostic interface; it determines if the conditions are right to run this diagnostic.
func (d PodCheckDns) CanRun() (bool, error) {
	return true, nil
}

// Check is part of the Diagnostic interface; it runs the actual diagnostic logic
func (d PodCheckDns) Check() types.DiagnosticResult {
	r := types.NewDiagnosticResult(PodCheckDnsName)

	if resolvConf, err := getResolvConf(r); err == nil {
		connectAndResolve(resolvConf, r)
		resolveSearch(resolvConf, r)
	}
	return r
}

func connectAndResolve(resolvConf *dns.ClientConfig, r types.DiagnosticResult) {
	for serverIndex, server := range resolvConf.Servers {
		// put together a DNS query to configured nameservers for kubernetes.default
		msg := new(dns.Msg)
		msg.SetQuestion("kubernetes.default.svc.cluster.local.", dns.TypeA)
		msg.RecursionDesired = false
		if result, completed := dnsQueryWithTimeout(msg, server, 2); !completed {
			if serverIndex == 0 { // in a pod, master (SkyDNS) IP is injected as first nameserver
				r.Warn("DP2009", nil, fmt.Sprintf("A request to the master (SkyDNS) nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.\nThis nameserver is critical for resolving cluster DNS names.", server))
			} else {
				r.Warn("DP2010", nil, fmt.Sprintf("A request to the nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.", server))
			}
		} else {
			in, err := result.in, result.err
			if serverIndex == 0 { // in a pod, master (SkyDNS) IP is injected as first nameserver
				if err != nil {
					r.Error("DP2003", err, fmt.Sprintf("The first /etc/resolv.conf nameserver %s\ncould not resolve kubernetes.default.svc.cluster.local.\nError: %v\nThis nameserver points to the master's SkyDNS which is critical for\nresolving cluster names, e.g. for Services.", server, err))
				} else if len(in.Answer) == 0 {
					r.Error("DP2006", err, fmt.Sprintf("The first /etc/resolv.conf nameserver %s\ncould not resolve kubernetes.default.svc.cluster.local.\nReturn code: %v\nThis nameserver points to the master's SkyDNS which is critical for\nresolving cluster names, e.g. for Services.", server, dns.RcodeToString[in.MsgHdr.Rcode]))
				} else {
					r.Debug("DP2007", fmt.Sprintf("The first /etc/resolv.conf nameserver %s\nresolved kubernetes.default.svc.cluster.local. to:\n  %s", server, in.Answer[0]))
				}
			} else if err != nil {
				r.Warn("DP2004", err, fmt.Sprintf("Error querying nameserver %s:\n  %v\nThis may indicate a problem with non-cluster DNS.", server, err))
			} else {
				rcode := in.MsgHdr.Rcode
				switch rcode {
				case dns.RcodeSuccess, dns.RcodeNameError: // aka NXDOMAIN
					r.Debug("DP2005", fmt.Sprintf("Successful query to nameserver %s", server))
				default:
					r.Warn("DP2008", nil, fmt.Sprintf("Received unexpected return code '%s' from nameserver %s:\nThis may indicate a problem with non-cluster DNS.", dns.RcodeToString[rcode], server))
				}
			}
		}
	}
}

const letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789"

func resolveSearch(resolvConf *dns.ClientConfig, r types.DiagnosticResult) {
	foundDomain := false
	randomString := func() string {
		b := make([]byte, 20)
		for i := range b {
			b[i] = letterBytes[rand.Intn(len(letterBytes))]
		}
		return string(b)
	}()
	seenDP2014 := sets.String{}
	seenDP2015 := sets.String{}
	for _, domain := range resolvConf.Search {
		if domain == "svc.cluster.local" {
			foundDomain = true // this will make kubernetes.default work
		}
		// put together a DNS query to configured nameservers for each search domain
		msg := new(dns.Msg)
		msg.SetQuestion("wildcard."+randomString+"."+domain+".", dns.TypeA)
		msg.RecursionDesired = true // otherwise we just get the authority section for the TLD
		for _, server := range resolvConf.Servers {
			result, completed := dnsQueryWithTimeout(msg, server, 2)
			switch {
			case !completed:
				if !seenDP2014.Has(server) {
					r.Warn("DP2014", nil, fmt.Sprintf("A request to the nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.", server))
					seenDP2014.Insert(server) // no need to keep warning about the same server for every domain
				}
			case result.err != nil:
				if !seenDP2015.Has(server) {
					r.Warn("DP2015", result.err, fmt.Sprintf("Error querying nameserver %s:\n  %v\nThis may indicate a problem with DNS.", server, result.err))
					seenDP2015.Insert(server) // don't repeat the error for the same nameserver; chances are it's the same error
				}
			case result.in.Answer == nil, len(result.in.Answer) == 0:
				r.Debug("DP2017", fmt.Sprintf("Nameserver %s responded to wildcard with no answer, which is expected.\n%v", server, result.in))
			default: // the random domain is not supposed to resolve
				r.Error("DP2016", nil, fmt.Sprintf(txtDP2016, server, domain, result.in))
			}
		}
	}
	if !foundDomain {
		r.Error("DP2019", nil, "Did not find svc.cluster.local among the configured search domains in /etc/resolv.conf.\nThis is likely to cause problems with certain components that expect to use partial cluster addresses.")
	}
}
<commit_msg>Fix dns lookup in PodCheckDns diagnostic<commit_after>package pod

import (
	"fmt"
	"math/rand"
	"net"

	"github.com/miekg/dns"
	"github.com/openshift/origin/pkg/diagnostics/types"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	PodCheckDnsName = "PodCheckDns"
	txtDP2016       = `
Query to nameserver %s for random subdomain of %s
returned an answer, indicating that this domain provides wildcard DNS.

This domain should not be in the search.

It can cause queries for external domains (e.g. github.com)
to resolve to the wildcard address. Full response:

%v`
)

// PodCheckDns is a Diagnostic to check that DNS within a pod works as expected
type PodCheckDns struct {
}

// Name is part of the Diagnostic interface and just returns name.
func (d PodCheckDns) Name() string {
	return PodCheckDnsName
}

// Description is part of the Diagnostic interface and just returns the diagnostic description.
func (d PodCheckDns) Description() string {
	return "Check that DNS within a pod works as expected"
}

// CanRun is part of the Diagnostic interface; it determines if the conditions are right to run this diagnostic.
func (d PodCheckDns) CanRun() (bool, error) {
	return true, nil
}

// Check is part of the Diagnostic interface; it runs the actual diagnostic logic
func (d PodCheckDns) Check() types.DiagnosticResult {
	r := types.NewDiagnosticResult(PodCheckDnsName)

	if resolvConf, err := getResolvConf(r); err == nil {
		dnsServers := sanitizeDNSServers(resolvConf.Servers)
		connectAndResolve(resolvConf, dnsServers, r)
		resolveSearch(resolvConf, dnsServers, r)
	}
	return r
}

// sanitizeDNSServers does these things:
// - Reorder dns servers: ipv4 servers are ahead of ipv6 servers as we only support ipv4 addrs at this time
// - Put all dns servers in square brackets: This will disambiguate addr and port when there are many colons in the addr
func sanitizeDNSServers(servers []string) []string {
	ipv4Servers := []string{}
	ipv6Servers := []string{}

	for _, server := range servers {
		if ip := net.ParseIP(server); ip != nil {
			if ip.To4() != nil {
				ipv4Servers = append(ipv4Servers, fmt.Sprintf("[%s]", server))
			} else {
				ipv6Servers = append(ipv6Servers, fmt.Sprintf("[%s]", server))
			}
		}
	}

	return append(ipv4Servers, ipv6Servers...)
}

func connectAndResolve(resolvConf *dns.ClientConfig, dnsServers []string, r types.DiagnosticResult) {
	for serverIndex, server := range dnsServers {
		// put together a DNS query to configured nameservers for kubernetes.default
		msg := new(dns.Msg)
		msg.SetQuestion("kubernetes.default.svc.cluster.local.", dns.TypeA)
		msg.RecursionDesired = false
		if result, completed := dnsQueryWithTimeout(msg, server, 2); !completed {
			if serverIndex == 0 { // in a pod, master (SkyDNS) IP is injected as first nameserver
				r.Warn("DP2009", nil, fmt.Sprintf("A request to the master (SkyDNS) nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.\nThis nameserver is critical for resolving cluster DNS names.", server))
			} else {
				r.Warn("DP2010", nil, fmt.Sprintf("A request to the nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.", server))
			}
		} else {
			in, err := result.in, result.err
			if serverIndex == 0 { // in a pod, master (SkyDNS) IP is injected as first nameserver
				if err != nil {
					r.Error("DP2003", err, fmt.Sprintf("The first /etc/resolv.conf nameserver %s\ncould not resolve kubernetes.default.svc.cluster.local.\nError: %v\nThis nameserver points to the master's SkyDNS which is critical for\nresolving cluster names, e.g. for Services.", server, err))
				} else if len(in.Answer) == 0 {
					r.Error("DP2006", err, fmt.Sprintf("The first /etc/resolv.conf nameserver %s\ncould not resolve kubernetes.default.svc.cluster.local.\nReturn code: %v\nThis nameserver points to the master's SkyDNS which is critical for\nresolving cluster names, e.g. for Services.", server, dns.RcodeToString[in.MsgHdr.Rcode]))
				} else {
					r.Debug("DP2007", fmt.Sprintf("The first /etc/resolv.conf nameserver %s\nresolved kubernetes.default.svc.cluster.local. to:\n  %s", server, in.Answer[0]))
				}
			} else if err != nil {
				r.Warn("DP2004", err, fmt.Sprintf("Error querying nameserver %s:\n  %v\nThis may indicate a problem with non-cluster DNS.", server, err))
			} else {
				rcode := in.MsgHdr.Rcode
				switch rcode {
				case dns.RcodeSuccess, dns.RcodeNameError: // aka NXDOMAIN
					r.Debug("DP2005", fmt.Sprintf("Successful query to nameserver %s", server))
				default:
					r.Warn("DP2008", nil, fmt.Sprintf("Received unexpected return code '%s' from nameserver %s:\nThis may indicate a problem with non-cluster DNS.", dns.RcodeToString[rcode], server))
				}
			}
		}
	}
}

const letterBytes = "abcdefghijklmnopqrstuvwxyz0123456789"

func resolveSearch(resolvConf *dns.ClientConfig, dnsServers []string, r types.DiagnosticResult) {
	foundDomain := false
	randomString := func() string {
		b := make([]byte, 20)
		for i := range b {
			b[i] = letterBytes[rand.Intn(len(letterBytes))]
		}
		return string(b)
	}()
	seenDP2014 := sets.String{}
	seenDP2015 := sets.String{}
	for _, domain := range resolvConf.Search {
		if domain == "svc.cluster.local" {
			foundDomain = true // this will make kubernetes.default work
		}
		// put together a DNS query to configured nameservers for each search domain
		msg := new(dns.Msg)
		msg.SetQuestion("wildcard."+randomString+"."+domain+".", dns.TypeA)
		msg.RecursionDesired = true // otherwise we just get the authority section for the TLD
		for _, server := range dnsServers {
			result, completed := dnsQueryWithTimeout(msg, server, 2)
			switch {
			case !completed:
				if !seenDP2014.Has(server) {
					r.Warn("DP2014", nil, fmt.Sprintf("A request to the nameserver %s timed out.\nThis could be temporary but could also indicate network or DNS problems.", server))
					seenDP2014.Insert(server) // no need to keep warning about the same server for every domain
				}
			case result.err != nil:
				if !seenDP2015.Has(server) {
					r.Warn("DP2015", result.err, fmt.Sprintf("Error querying nameserver %s:\n  %v\nThis may indicate a problem with DNS.", server, result.err))
					seenDP2015.Insert(server) // don't repeat the error for the same nameserver; chances are it's the same error
				}
			case result.in.Answer == nil, len(result.in.Answer) == 0:
				r.Debug("DP2017", fmt.Sprintf("Nameserver %s responded to wildcard with no answer, which is expected.\n%v", server, result.in))
			default: // the random domain is not supposed to resolve
				r.Error("DP2016", nil, fmt.Sprintf(txtDP2016, server, domain, result.in))
			}
		}
	}
	if !foundDomain {
		r.Error("DP2019", nil, "Did not find svc.cluster.local among the configured search domains in /etc/resolv.conf.\nThis is likely to cause problems with certain components that expect to use partial cluster addresses.")
	}
}
<|endoftext|>


<commit_before>// Copyright Istio 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 queue

import (
	"errors"
	"sync"
	"testing"
	"time"

	"github.com/cenkalti/backoff/v4"
	"go.uber.org/atomic"
)

func TestOrdering(t *testing.T) {
	numValues := 1000

	q := NewQueue(1 * time.Microsecond)
	stop := make(chan struct{})
	defer close(stop)

	wg := sync.WaitGroup{}
	wg.Add(numValues)
	mu := sync.Mutex{}
	out := make([]int, 0)
	for i := 0; i < numValues; i++ {
		i := i

		q.Push(func() error {
			mu.Lock()
			out = append(out, i)
			defer mu.Unlock()
			wg.Done()
			return nil
		})

		// Start the queue at the halfway point.
		if i == numValues/2 {
			go q.Run(stop)
		}
	}

	// wait for all task processed
	wg.Wait()

	if len(out) != numValues {
		t.Fatalf("expected output array length %d to equal %d", len(out), numValues)
	}

	for i := 0; i < numValues; i++ {
		if i != out[i] {
			t.Fatalf("expected out[%d] %v to equal %v", i, out[i], i)
		}
	}
}

func TestRetry(t *testing.T) {
	q := NewQueue(1 * time.Microsecond)
	stop := make(chan struct{})
	defer close(stop)

	// Push a task that fails the first time and retries.
	wg := sync.WaitGroup{}
	wg.Add(2)
	failed := false
	q.Push(func() error {
		defer wg.Done()
		if failed {
			return nil
		}
		failed = true
		return errors.New("fake error")
	})

	go q.Run(stop)

	// wait for the task to run twice.
	wg.Wait()
}

func TestRetryWithBackoff(t *testing.T) {
	ebf := backoff.NewExponentialBackOff()
	ebf.InitialInterval = 1 * time.Microsecond
	ebf.MaxInterval = 5 * time.Microsecond
	q := NewBackOffQueue(ebf)
	stop := make(chan struct{})
	defer close(stop)

	// Push a task that fails the first time and retries.
	wg := sync.WaitGroup{}
	wg.Add(2)
	failed := false
	q.Push(func() error {
		defer wg.Done()
		if failed {
			return nil
		}
		failed = true
		return errors.New("fake error")
	})

	go q.Run(stop)

	// wait for the task to run twice.
	wg.Wait()
}

func TestResourceFree(t *testing.T) {
	q := NewQueue(1 * time.Microsecond)
	stop := make(chan struct{})
	signal := make(chan struct{})
	go func() {
		q.Run(stop)
		signal <- struct{}{}
	}()

	q.Push(func() error {
		t.Log("mock exec")
		return nil
	})

	// mock queue block wait cond signal
	time.AfterFunc(10*time.Millisecond, func() {
		close(stop)
	})

	select {
	case <-time.After(200 * time.Millisecond):
		t.Error("close stop, method exit timeout.")
	case <-signal:
		t.Log("queue return.")
	}
}

func TestClosed(t *testing.T) {
	t.Run("immediate close", func(t *testing.T) {
		stop := make(chan struct{})
		q := NewQueue(0)
		go q.Run(stop)
		close(stop)
		if err := WaitForClose(q, 10*time.Second); err != nil {
			t.Error(err)
		}
	})
	t.Run("close after tasks", func(t *testing.T) {
		stop := make(chan struct{})
		q := NewQueue(0)
		taskComplete := atomic.NewBool(false)
		q.Push(func() error {
			taskComplete.Store(true)
			return nil
		})
		go q.Run(stop)
		close(stop)
		if err := WaitForClose(q, 10*time.Second); err != nil {
			t.Error(err)
		}
		if !taskComplete.Load() {
			t.Error("task did not complete")
		}
	})
}
<commit_msg>fix flake: queue.TestClosed (#37102)<commit_after>// Copyright Istio 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 queue

import (
	"errors"
	"sync"
	"testing"
	"time"

	"github.com/cenkalti/backoff/v4"
	"go.uber.org/atomic"
)

func TestOrdering(t *testing.T) {
	numValues := 1000

	q := NewQueue(1 * time.Microsecond)
	stop := make(chan struct{})
	defer close(stop)

	wg := sync.WaitGroup{}
	wg.Add(numValues)
	mu := sync.Mutex{}
	out := make([]int, 0)
	for i := 0; i < numValues; i++ {
		i := i

		q.Push(func() error {
			mu.Lock()
			out = append(out, i)
			defer mu.Unlock()
			wg.Done()
			return nil
		})

		// Start the queue at the halfway point.
		if i == numValues/2 {
			go q.Run(stop)
		}
	}

	// wait for all task processed
	wg.Wait()

	if len(out) != numValues {
		t.Fatalf("expected output array length %d to equal %d", len(out), numValues)
	}

	for i := 0; i < numValues; i++ {
		if i != out[i] {
			t.Fatalf("expected out[%d] %v to equal %v", i, out[i], i)
		}
	}
}

func TestRetry(t *testing.T) {
	q := NewQueue(1 * time.Microsecond)
	stop := make(chan struct{})
	defer close(stop)

	// Push a task that fails the first time and retries.
	wg := sync.WaitGroup{}
	wg.Add(2)
	failed := false
	q.Push(func() error {
		defer wg.Done()
		if failed {
			return nil
		}
		failed = true
		return errors.New("fake error")
	})

	go q.Run(stop)

	// wait for the task to run twice.
	wg.Wait()
}

func TestRetryWithBackoff(t *testing.T) {
	ebf := backoff.NewExponentialBackOff()
	ebf.InitialInterval = 1 * time.Microsecond
	ebf.MaxInterval = 5 * time.Microsecond
	q := NewBackOffQueue(ebf)
	stop := make(chan struct{})
	defer close(stop)

	// Push a task that fails the first time and retries.
	wg := sync.WaitGroup{}
	wg.Add(2)
	failed := false
	q.Push(func() error {
		defer wg.Done()
		if failed {
			return nil
		}
		failed = true
		return errors.New("fake error")
	})

	go q.Run(stop)

	// wait for the task to run twice.
	wg.Wait()
}

func TestResourceFree(t *testing.T) {
	q := NewQueue(1 * time.Microsecond)
	stop := make(chan struct{})
	signal := make(chan struct{})
	go func() {
		q.Run(stop)
		signal <- struct{}{}
	}()

	q.Push(func() error {
		t.Log("mock exec")
		return nil
	})

	// mock queue block wait cond signal
	time.AfterFunc(10*time.Millisecond, func() {
		close(stop)
	})

	select {
	case <-time.After(200 * time.Millisecond):
		t.Error("close stop, method exit timeout.")
	case <-signal:
		t.Log("queue return.")
	}
}

func TestClosed(t *testing.T) {
	t.Run("immediate close", func(t *testing.T) {
		stop := make(chan struct{})
		q := NewQueue(0)
		go q.Run(stop)
		close(stop)
		if err := WaitForClose(q, 10*time.Second); err != nil {
			t.Error(err)
		}
	})
	t.Run("no tasks after close", func(t *testing.T) {
		stop := make(chan struct{})
		q := NewQueue(0)
		taskComplete := atomic.NewBool(false)
		q.Push(func() error {
			close(stop)
			return nil
		})
		go q.Run(stop)
		if err := WaitForClose(q, 10*time.Second); err != nil {
			t.Error()
		}
		q.Push(func() error {
			taskComplete.Store(true)
			return nil
		})
		if taskComplete.Load() {
			t.Error("task ran on closed queue")
		}
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 tiller

import (
	"fmt"
	"regexp"

	"k8s.io/helm/pkg/proto/hapi/release"
	"k8s.io/helm/pkg/proto/hapi/services"
	relutil "k8s.io/helm/pkg/releaseutil"
)

// ListReleases lists the releases found by the server.
func (s *ReleaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error {
	if len(req.StatusCodes) == 0 {
		req.StatusCodes = []release.Status_Code{release.Status_DEPLOYED}
	}

	//rels, err := s.env.Releases.ListDeployed()
	rels, err := s.env.Releases.ListFilterAll(func(r *release.Release) bool {
		for _, sc := range req.StatusCodes {
			if sc == r.Info.Status.Code {
				return true
			}
		}
		return false
	})
	if err != nil {
		return err
	}

	if req.Namespace != "" {
		rels, err = filterByNamespace(req.Namespace, rels)
		if err != nil {
			return err
		}
	}

	if len(req.Filter) != 0 {
		rels, err = filterReleases(req.Filter, rels)
		if err != nil {
			return err
		}
	}

	total := int64(len(rels))

	switch req.SortBy {
	case services.ListSort_NAME:
		relutil.SortByName(rels)
	case services.ListSort_LAST_RELEASED:
		relutil.SortByDate(rels)
	}

	if req.SortOrder == services.ListSort_DESC {
		ll := len(rels)
		rr := make([]*release.Release, ll)
		for i, item := range rels {
			rr[ll-i-1] = item
		}
		rels = rr
	}

	l := int64(len(rels))
	if req.Offset != "" {

		i := -1
		for ii, cur := range rels {
			if cur.Name == req.Offset {
				i = ii
			}
		}
		if i == -1 {
			return fmt.Errorf("offset %q not found", req.Offset)
		}

		if len(rels) < i {
			return fmt.Errorf("no items after %q", req.Offset)
		}

		rels = rels[i:]
		l = int64(len(rels))
	}

	if req.Limit == 0 {
		req.Limit = ListDefaultLimit
	}

	next := ""
	if l > req.Limit {
		next = rels[req.Limit].Name
		rels = rels[0:req.Limit]
		l = int64(len(rels))
	}

	for i := 0; i < min(len(rels), int(req.Limit)); i++ {
		res := &services.ListReleasesResponse{
			Next:     next,
			Count:    l,
			Total:    total,
			Releases: []*release.Release{rels[i]},
		}
		if err := stream.Send(res); err != nil {
			return err
		}
	}
	return nil
}

func filterByNamespace(namespace string, rels []*release.Release) ([]*release.Release, error) {
	matches := []*release.Release{}
	for _, r := range rels {
		if namespace == r.Namespace {
			matches = append(matches, r)
		}
	}
	return matches, nil
}

func filterReleases(filter string, rels []*release.Release) ([]*release.Release, error) {
	preg, err := regexp.Compile(filter)
	if err != nil {
		return rels, err
	}
	matches := []*release.Release{}
	for _, r := range rels {
		if preg.MatchString(r.Name) {
			matches = append(matches, r)
		}
	}
	return matches, nil
}
<commit_msg>release list paritioning<commit_after>/*
Copyright 2016 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 tiller

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"k8s.io/helm/pkg/proto/hapi/release"
	"k8s.io/helm/pkg/proto/hapi/services"
	relutil "k8s.io/helm/pkg/releaseutil"
	"regexp"
)

// ListReleases lists the releases found by the server.
func (s *ReleaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error {
	if len(req.StatusCodes) == 0 {
		req.StatusCodes = []release.Status_Code{release.Status_DEPLOYED}
	}

	//rels, err := s.env.Releases.ListDeployed()
	rels, err := s.env.Releases.ListFilterAll(func(r *release.Release) bool {
		for _, sc := range req.StatusCodes {
			if sc == r.Info.Status.Code {
				return true
			}
		}
		return false
	})
	if err != nil {
		return err
	}

	if req.Namespace != "" {
		rels, err = filterByNamespace(req.Namespace, rels)
		if err != nil {
			return err
		}
	}

	if len(req.Filter) != 0 {
		rels, err = filterReleases(req.Filter, rels)
		if err != nil {
			return err
		}
	}

	total := int64(len(rels))

	switch req.SortBy {
	case services.ListSort_NAME:
		relutil.SortByName(rels)
	case services.ListSort_LAST_RELEASED:
		relutil.SortByDate(rels)
	}

	if req.SortOrder == services.ListSort_DESC {
		ll := len(rels)
		rr := make([]*release.Release, ll)
		for i, item := range rels {
			rr[ll-i-1] = item
		}
		rels = rr
	}

	l := int64(len(rels))
	if req.Offset != "" {

		i := -1
		for ii, cur := range rels {
			if cur.Name == req.Offset {
				i = ii
			}
		}
		if i == -1 {
			return fmt.Errorf("offset %q not found", req.Offset)
		}

		if len(rels) < i {
			return fmt.Errorf("no items after %q", req.Offset)
		}

		rels = rels[i:]
		l = int64(len(rels))
	}

	if req.Limit == 0 {
		req.Limit = ListDefaultLimit
	}

	next := ""
	if l > req.Limit {
		next = rels[req.Limit].Name
		rels = rels[0:req.Limit]
		l = int64(len(rels))
	}
	res := &services.ListReleasesResponse{
		Next:  next,
		Count: l,
		Total: total,
	}
	chunks := s.partition(rels[:min(len(rels), int(req.Limit))], maxMsgSize-proto.Size(res))
	for res.Releases = range chunks {
		if err := stream.Send(res); err != nil {
			for range chunks { // drain
			}
			return err
		}
	}
	return nil
}

// partition packs releases into slices upto the capacity cap in bytes.
func (s *ReleaseServer) partition(rels []*release.Release, cap int) <-chan []*release.Release {
	chunks := make(chan []*release.Release, 1)
	go func() {
		var (
			fill = 0 // fill is space available to fill
			size int // size is size of a release
		)
		var chunk []*release.Release
		for _, rls := range rels {
			if size = proto.Size(rls); size+fill > cap {
				// Over-cap, push chunk onto channel to send over gRPC stream
				s.Log("partitioned at %d with %d releases (cap=%d)", fill, len(chunk), cap)
				chunks <- chunk
				// reset paritioning state
				chunk = chunk[:0]
				fill = 0
			}
			chunk = append(chunk, rls)
			fill += size
		}
		if len(chunk) > 0 {
			// send remaining if any
			chunks <- chunk
		}
		close(chunks)
	}()
	return chunks
}

func filterByNamespace(namespace string, rels []*release.Release) ([]*release.Release, error) {
	matches := []*release.Release{}
	for _, r := range rels {
		if namespace == r.Namespace {
			matches = append(matches, r)
		}
	}
	return matches, nil
}

func filterReleases(filter string, rels []*release.Release) ([]*release.Release, error) {
	preg, err := regexp.Compile(filter)
	if err != nil {
		return rels, err
	}
	matches := []*release.Release{}
	for _, r := range rels {
		if preg.MatchString(r.Name) {
			matches = append(matches, r)
		}
	}
	return matches, nil
}
<|endoftext|>


<commit_before>package main

import (
	"doozer"
	"flag"
	"fmt"
	"net"
	"os"
	"log"
	_ "expvar"
	_ "http/pprof"
)

var (
	listenAddr  = flag.String("l", "127.0.0.1:8046", "The address to bind to.")
	attachAddr  = flag.String("a", "", "The address of another node to attach to.")
	webAddr     = flag.String("w", ":8080", "Serve web requests on this address.")
	clusterName = flag.String("c", "local", "The non-empty cluster name.")
	showVersion = flag.Bool("v", false, "print doozerd's version string")
	pi          = flag.Float64("pulse", 1, "how often (in seconds) to set applied key")
	fd          = flag.Float64("fill", 1.5, "delay (in seconds) to fill unowned seqns")
	kt          = flag.Float64("timeout", 60, "timeout (in seconds) to kick inactive nodes")
)

func Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\nOptions:\n")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = Usage
	flag.Parse()

	if *showVersion {
		fmt.Println("doozerd", doozer.Version)
		return
	}

	if *listenAddr == "" {
		fmt.Fprintln(os.Stderr, "require a listen address")
		flag.Usage()
		os.Exit(1)
	}

	log.SetPrefix("DOOZER ")
	log.SetFlags(log.Ldate | log.Lmicroseconds)

	listener, err := net.Listen("tcp", *listenAddr)
	if err != nil {
		panic(err)
	}

	conn, err := net.ListenPacket("udp", *listenAddr)
	if err != nil {
		panic(err)
	}

	var wl net.Listener
	if *webAddr != "" {
		wl, err = net.Listen("tcp", *webAddr)
		if err != nil {
			panic(err)
		}
	}

	doozer.Main(*clusterName, *attachAddr, conn, listener, wl, ns(*pi), ns(*fd), ns(*kt))
	panic("main exit")
}

func ns(x float64) int64 {
	return int64(x * 1e9)
}
<commit_msg>doozerd: make default -fill more reasonable for local use<commit_after>package main

import (
	"doozer"
	"flag"
	"fmt"
	"net"
	"os"
	"log"
	_ "expvar"
	_ "http/pprof"
)

var (
	listenAddr  = flag.String("l", "127.0.0.1:8046", "The address to bind to.")
	attachAddr  = flag.String("a", "", "The address of another node to attach to.")
	webAddr     = flag.String("w", ":8080", "Serve web requests on this address.")
	clusterName = flag.String("c", "local", "The non-empty cluster name.")
	showVersion = flag.Bool("v", false, "print doozerd's version string")
	pi          = flag.Float64("pulse", 1, "how often (in seconds) to set applied key")
	fd          = flag.Float64("fill", .1, "delay (in seconds) to fill unowned seqns")
	kt          = flag.Float64("timeout", 60, "timeout (in seconds) to kick inactive nodes")
)

func Usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\nOptions:\n")
	flag.PrintDefaults()
}

func main() {
	flag.Usage = Usage
	flag.Parse()

	if *showVersion {
		fmt.Println("doozerd", doozer.Version)
		return
	}

	if *listenAddr == "" {
		fmt.Fprintln(os.Stderr, "require a listen address")
		flag.Usage()
		os.Exit(1)
	}

	log.SetPrefix("DOOZER ")
	log.SetFlags(log.Ldate | log.Lmicroseconds)

	listener, err := net.Listen("tcp", *listenAddr)
	if err != nil {
		panic(err)
	}

	conn, err := net.ListenPacket("udp", *listenAddr)
	if err != nil {
		panic(err)
	}

	var wl net.Listener
	if *webAddr != "" {
		wl, err = net.Listen("tcp", *webAddr)
		if err != nil {
			panic(err)
		}
	}

	doozer.Main(*clusterName, *attachAddr, conn, listener, wl, ns(*pi), ns(*fd), ns(*kt))
	panic("main exit")
}

func ns(x float64) int64 {
	return int64(x * 1e9)
}
<|endoftext|>


<commit_before>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"cmd/internal/objfile"
	"debug/dwarf"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string) {
	testenv.MustHaveCGO(t)
	testenv.MustHaveGoBuild(t)

	if runtime.GOOS == "plan9" {
		t.Skip("skipping on plan9; no DWARF symbol table in executables")
	}

	out, err := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "cmd/link").CombinedOutput()
	if err != nil {
		t.Fatalf("go list: %v\n%s", err, out)
	}
	if string(out) != "false\n" {
		if os.Getenv("GOROOT_FINAL_OLD") != "" {
			t.Skip("cmd/link is stale, but $GOROOT_FINAL_OLD is set")
		}
		t.Fatalf("cmd/link is stale - run go install cmd/link")
	}

	tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF")
	if err != nil {
		t.Fatal("TempDir failed: ", err)
	}
	defer os.RemoveAll(tmpDir)

	for _, prog := range []string{"testprog", "testprogcgo"} {
		prog := prog
		t.Run(prog, func(t *testing.T) {
			t.Parallel()

			exe := filepath.Join(tmpDir, prog+".exe")
			dir := "../../runtime/testdata/" + prog
			cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
			if buildmode != "" {
				cmd.Args = append(cmd.Args, "-buildmode", buildmode)
			}
			cmd.Args = append(cmd.Args, dir)
			if env != nil {
				cmd.Env = append(os.Environ(), env...)
			}
			out, err := cmd.CombinedOutput()
			if err != nil {
				t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
			}

			if buildmode == "c-archive" {
				// Extract the archive and use the go.o object within.
				cmd := exec.Command("ar", "-x", exe)
				cmd.Dir = tmpDir
				if out, err := cmd.CombinedOutput(); err != nil {
					t.Fatalf("ar -x %s: %v\n%s", exe, err, out)
				}
				exe = filepath.Join(tmpDir, "go.o")
			}
			f, err := objfile.Open(exe)
			if err != nil {
				t.Fatal(err)
			}
			defer f.Close()

			syms, err := f.Symbols()
			if err != nil {
				t.Fatal(err)
			}

			var addr uint64
			for _, sym := range syms {
				if sym.Name == "main.main" {
					addr = sym.Addr
					break
				}
			}
			if addr == 0 {
				t.Fatal("cannot find main.main in symbols")
			}

			d, err := f.DWARF()
			if err != nil {
				if expectDWARF {
					t.Fatal(err)
				}
				return
			} else {
				if !expectDWARF {
					t.Fatal("unexpected DWARF section")
				}
			}

			// TODO: We'd like to use filepath.Join here.
			// Also related: golang.org/issue/19784.
			wantFile := path.Join(prog, "main.go")
			wantLine := 24
			r := d.Reader()
			entry, err := r.SeekPC(addr)
			if err != nil {
				t.Fatal(err)
			}
			lr, err := d.LineReader(entry)
			if err != nil {
				t.Fatal(err)
			}
			var line dwarf.LineEntry
			if err := lr.SeekPC(addr, &line); err == dwarf.ErrUnknownPC {
				t.Fatalf("did not find file:line for %#x (main.main)", addr)
			} else if err != nil {
				t.Fatal(err)
			}
			if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
				t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
			}
		})
	}
}

func TestDWARF(t *testing.T) {
	testDWARF(t, "", true)
}

func TestDWARFiOS(t *testing.T) {
	// Normally we run TestDWARF on native platform. But on iOS we don't have
	// go build, so we do this test with a cross build.
	// Only run this on darwin/amd64, where we can cross build for iOS.
	if testing.Short() {
		t.Skip("skipping in short mode")
	}
	if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
		t.Skip("skipping on non-darwin/amd64 platform")
	}
	if err := exec.Command("xcrun", "--help").Run(); err != nil {
		t.Skipf("error running xcrun, required for iOS cross build: %v", err)
	}
	cc := "CC=" + runtime.GOROOT() + "/misc/ios/clangwrap.sh"
	// iOS doesn't allow unmapped segments, so iOS executables don't have DWARF.
	testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
	testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
	// However, c-archive iOS objects have embedded DWARF.
	testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
	testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
}
<commit_msg>cmd/link: reliably remove temporary directory in testDwarf<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"cmd/internal/objfile"
	"debug/dwarf"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string) {
	testenv.MustHaveCGO(t)
	testenv.MustHaveGoBuild(t)

	if runtime.GOOS == "plan9" {
		t.Skip("skipping on plan9; no DWARF symbol table in executables")
	}

	out, err := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "cmd/link").CombinedOutput()
	if err != nil {
		t.Fatalf("go list: %v\n%s", err, out)
	}
	if string(out) != "false\n" {
		if os.Getenv("GOROOT_FINAL_OLD") != "" {
			t.Skip("cmd/link is stale, but $GOROOT_FINAL_OLD is set")
		}
		t.Fatalf("cmd/link is stale - run go install cmd/link")
	}

	for _, prog := range []string{"testprog", "testprogcgo"} {
		prog := prog
		t.Run(prog, func(t *testing.T) {
			t.Parallel()

			tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF")
			if err != nil {
				t.Fatal(err)
			}
			defer os.RemoveAll(tmpDir)

			exe := filepath.Join(tmpDir, prog+".exe")
			dir := "../../runtime/testdata/" + prog
			cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
			if buildmode != "" {
				cmd.Args = append(cmd.Args, "-buildmode", buildmode)
			}
			cmd.Args = append(cmd.Args, dir)
			if env != nil {
				cmd.Env = append(os.Environ(), env...)
			}
			out, err := cmd.CombinedOutput()
			if err != nil {
				t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
			}

			if buildmode == "c-archive" {
				// Extract the archive and use the go.o object within.
				cmd := exec.Command("ar", "-x", exe)
				cmd.Dir = tmpDir
				if out, err := cmd.CombinedOutput(); err != nil {
					t.Fatalf("ar -x %s: %v\n%s", exe, err, out)
				}
				exe = filepath.Join(tmpDir, "go.o")
			}
			f, err := objfile.Open(exe)
			if err != nil {
				t.Fatal(err)
			}
			defer f.Close()

			syms, err := f.Symbols()
			if err != nil {
				t.Fatal(err)
			}

			var addr uint64
			for _, sym := range syms {
				if sym.Name == "main.main" {
					addr = sym.Addr
					break
				}
			}
			if addr == 0 {
				t.Fatal("cannot find main.main in symbols")
			}

			d, err := f.DWARF()
			if err != nil {
				if expectDWARF {
					t.Fatal(err)
				}
				return
			} else {
				if !expectDWARF {
					t.Fatal("unexpected DWARF section")
				}
			}

			// TODO: We'd like to use filepath.Join here.
			// Also related: golang.org/issue/19784.
			wantFile := path.Join(prog, "main.go")
			wantLine := 24
			r := d.Reader()
			entry, err := r.SeekPC(addr)
			if err != nil {
				t.Fatal(err)
			}
			lr, err := d.LineReader(entry)
			if err != nil {
				t.Fatal(err)
			}
			var line dwarf.LineEntry
			if err := lr.SeekPC(addr, &line); err == dwarf.ErrUnknownPC {
				t.Fatalf("did not find file:line for %#x (main.main)", addr)
			} else if err != nil {
				t.Fatal(err)
			}
			if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
				t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
			}
		})
	}
}

func TestDWARF(t *testing.T) {
	testDWARF(t, "", true)
}

func TestDWARFiOS(t *testing.T) {
	// Normally we run TestDWARF on native platform. But on iOS we don't have
	// go build, so we do this test with a cross build.
	// Only run this on darwin/amd64, where we can cross build for iOS.
	if testing.Short() {
		t.Skip("skipping in short mode")
	}
	if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
		t.Skip("skipping on non-darwin/amd64 platform")
	}
	if err := exec.Command("xcrun", "--help").Run(); err != nil {
		t.Skipf("error running xcrun, required for iOS cross build: %v", err)
	}
	cc := "CC=" + runtime.GOROOT() + "/misc/ios/clangwrap.sh"
	// iOS doesn't allow unmapped segments, so iOS executables don't have DWARF.
	testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
	testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
	// However, c-archive iOS objects have embedded DWARF.
	testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
	testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
}
<|endoftext|>


<commit_before>package main

import (
	"erpel"
	"errors"
	"fmt"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
)

var showCmd = &cobra.Command{
	Use:     "show [flags] rulefile",
	Example: "$ erpel show /etc/erpel/rules.d/dovecot",
	Short:   "Parse and show a rules file",
	Long: `
The show command parses and visualises a file containing erpel ignore rules.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		return ShowRules(args)
	},
}

var (
	// show templates instead of field names
	displayTemplates bool

	// do not check against the rule samples
	ignoreRuleSamples bool
)

func init() {
	RootCmd.AddCommand(showCmd)

	showCmd.Flags().BoolVarP(&displayTemplates, "templates", "t", false, "show templates instead of field names")
	showCmd.Flags().BoolVarP(&ignoreRuleSamples, "ignore-samples", "I", false, "do not run check against rule samples")
}

var (
	printText        = color.New(color.FgWhite).PrintfFunc()
	printField       = color.New(color.FgHiRed).PrintfFunc()
	printGlobalField = color.New(color.FgHiBlue).PrintfFunc()
)

// ShowRules visualises an erpel rule file.
func ShowRules(args []string) error {
	if len(args) == 0 {
		return errors.New("no rule file specified, nothing to do")
	}

	if len(args) > 1 {
		return errors.New("more than one rule file specified")
	}

	filename := args[0]

	rules, err := erpel.ParseRulesFile(cfg.Fields, filename)
	if err != nil {
		return err
	}

	if !ignoreRuleSamples {
		if err = rules.Check(); err != nil {
			return err
		}
	}

	fmt.Printf("Rules from %v:\n", filename)
	for _, rv := range rules.Views() {
		for _, field := range rv {
			switch f := field.(type) {
			case erpel.Text:
				fmt.Printf("%s", f)
			case erpel.FieldView:
				p := printField
				if f.Global {
					p = printGlobalField
				}

				if displayTemplates {
					p("%s", f.S)
				} else {
					p("%s", f.F.Name)
				}
			}
		}
		fmt.Println()
	}

	if debugOutput {
		fmt.Printf("\nGenerated regexps:\n")
		for _, r := range rules.RegExps() {
			fmt.Printf("%s\n", r)
		}
	}

	return nil
}
<commit_msg>Print errors<commit_after>package main

import (
	"erpel"
	"errors"
	"fmt"
	"os"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
)

var showCmd = &cobra.Command{
	Use:     "show [flags] rulefile",
	Example: "$ erpel show /etc/erpel/rules.d/dovecot",
	Short:   "Parse and show a rules file",
	Long: `
The show command parses and visualises a file containing erpel ignore rules.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		return ShowRules(args)
	},
}

var (
	// show templates instead of field names
	displayTemplates bool

	// do not check against the rule samples
	ignoreRuleSamples bool
)

func init() {
	RootCmd.AddCommand(showCmd)

	showCmd.Flags().BoolVarP(&displayTemplates, "templates", "t", false, "show templates instead of field names")
	showCmd.Flags().BoolVarP(&ignoreRuleSamples, "ignore-samples", "I", false, "do not run check against rule samples")
}

var (
	printText        = color.New(color.FgWhite).PrintfFunc()
	printField       = color.New(color.FgHiRed).PrintfFunc()
	printGlobalField = color.New(color.FgHiBlue).PrintfFunc()
)

// ShowRules visualises an erpel rule file.
func ShowRules(args []string) error {
	if len(args) == 0 {
		return errors.New("no rule file specified, nothing to do")
	}

	if len(args) > 1 {
		return errors.New("more than one rule file specified")
	}

	filename := args[0]

	rules, err := erpel.ParseRulesFile(cfg.Fields, filename)
	if err != nil {
		return err
	}

	if err = rules.Check(); err != nil {
		if !ignoreRuleSamples {
			return err
		}
		fmt.Fprintf(os.Stderr, "error checking rules file: %v\n", err)
	}

	fmt.Printf("Rules from %v:\n", filename)
	for _, rv := range rules.Views() {
		for _, field := range rv {
			switch f := field.(type) {
			case erpel.Text:
				fmt.Printf("%s", f)
			case erpel.FieldView:
				p := printField
				if f.Global {
					p = printGlobalField
				}

				if displayTemplates {
					p("%s", f.S)
				} else {
					p("%s", f.F.Name)
				}
			}
		}
		fmt.Println()
	}

	if debugOutput {
		fmt.Printf("\nGenerated regexps:\n")
		for _, r := range rules.RegExps() {
			fmt.Printf("%s\n", r)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package colonycore

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
)

const (
	App_Command_Start         = "start"
	App_Command_Stop          = "stop"
	App_Command_RunningStatus = "running_status"
	App_Command_DeployStatus  = "deploy_status"
	App_Variable_BinaryName   = "BINARY_NAME"
)

type Application struct {
	orm.ModelBase
	ID            string `json:"_id",bson:"_id"`
	IsInternalApp bool
	AppsName      string
	Port          string // for web type
	Type          string
	ZipName       string
	Enable        bool
	DeployedTo    []string
	Command       interface{}
	Variable      interface{}
}

func (a *Application) TableName() string {
	return "applications"
}

func (a *Application) RecordID() interface{} {
	return a.ID
}

type TreeSource struct {
	ID             int           `json:"_id",bson:"_id"`
	Text           string        `json:"text",bson:"text"`
	Expanded       bool          `json:"expanded",bson:"expanded"`
	SpriteCssClass string        `json:"spriteCssClass",bson:"spriteCssClass"`
	Items          []*TreeSource `json:"items",bson:"items"`
}

type TreeSourceModel struct {
	Text      string             `json:"text"`
	Type      string             `json:"type"`
	Expanded  bool               `json:"expanded"`
	Iconclass string             `json:"iconclass"`
	Ext       string             `json:"ext"`
	Path      string             `json:"path"`
	Items     []*TreeSourceModel `json:"items"`
}

func (a *Application) GetCommand(cmdName string) (bool, string) {
	cmdString := ""

	for _, raw := range a.Command.([]interface{}) {
		each := raw.(map[string]interface{})
		if each["key"].(string) == cmdName {
			cmdString = each["value"].(string)
			break
		}
	}

	for _, raw := range a.Variable.([]interface{}) {
		each := raw.(map[string]interface{})
		if strings.Contains(cmdString, each["key"].(string)) {
			cmdString = strings.Replace(cmdString, each["key"].(string), each["value"].(string), -1)
		}
	}

	if cmdString == "" {
		return false, ""
	}

	return true, cmdString
}

func (a *Application) GetAllInternalApps() ([]Application, error) {
	cursor, err := Find(a, dbox.Eq("IsInternalApp", true))
	if err != nil {
		return nil, err
	}

	data := make([]Application, 0)
	err = cursor.Fetch(&data, 0, true)
	if err != nil {
		return nil, err
	}

	return data, nil
}

func (a *Application) UpdateDeployedInfo(serverID string, mode string) error {
	cache := make(map[string]bool, 0)
	deployedTo := make([]string, 0)

	for _, each := range a.DeployedTo {
		if _, ok := cache[each]; ok {
			continue
		}

		if each == serverID {
			continue
		}

		cache[each] = true
		deployedTo = append(deployedTo, each)
	}

	if mode == "add" {
		deployedTo = append(deployedTo, serverID)
	}

	a.DeployedTo = deployedTo
	return Save(a)
}

func (a *Application) RunApp(server *Server) (bool, error) {
	sshSetting, _, err := server.Connect()
	if err != nil {
		return false, err
	}

	runWithTimeout := func(cmd string) (string, error) {
		timeout := time.Second * time.Duration(5)
		cRunCommand := make(chan string, 1)
		var res string

		go func(res *string) {
			temp, err := sshSetting.RunCommandSshAsMap(cmd)
			*res = temp[0].Output

			if err != nil {
				cRunCommand <- err.Error()
			} else {
				cRunCommand <- ""
			}
		}(&res)

		errorMessage := ""
		select {
		case receiveRunCommandOutput := <-cRunCommand:
			errorMessage = receiveRunCommandOutput
		case <-time.After(time.Second * time.Duration(timeout)):
			errorMessage = ""
		}

		if strings.TrimSpace(errorMessage) != "" {
			return "", errors.New(errorMessage)
		}

		return strings.TrimSpace(res), nil
	}

	cmdRun := fmt.Sprintf("cd $EC_APP_PATH/web && chmod 755 %s && ./%s &", a.ID, a.ID)
	output, err := runWithTimeout(cmdRun)
	if err != nil {
		return false, err
	}

	cmdPID := fmt.Sprintf("pidof %s", a.ID)
	output, err = runWithTimeout(cmdPID)
	if err != nil {
		return false, err
	}

	isRun := (strings.TrimSpace(output) != "")
	return isRun, nil
}
<commit_msg>no message<commit_after>package colonycore

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/sshclient"
)

const (
	App_Command_Start         = "start"
	App_Command_Stop          = "stop"
	App_Command_RunningStatus = "running_status"
	App_Command_DeployStatus  = "deploy_status"
	App_Variable_BinaryName   = "BINARY_NAME"
)

type Application struct {
	orm.ModelBase
	ID            string `json:"_id",bson:"_id"`
	IsInternalApp bool
	AppsName      string
	Port          string // for web type
	Type          string
	ZipName       string
	Enable        bool
	DeployedTo    []string
	Command       interface{}
	Variable      interface{}
}

func (a *Application) TableName() string {
	return "applications"
}

func (a *Application) RecordID() interface{} {
	return a.ID
}

type TreeSource struct {
	ID             int           `json:"_id",bson:"_id"`
	Text           string        `json:"text",bson:"text"`
	Expanded       bool          `json:"expanded",bson:"expanded"`
	SpriteCssClass string        `json:"spriteCssClass",bson:"spriteCssClass"`
	Items          []*TreeSource `json:"items",bson:"items"`
}

type TreeSourceModel struct {
	Text      string             `json:"text"`
	Type      string             `json:"type"`
	Expanded  bool               `json:"expanded"`
	Iconclass string             `json:"iconclass"`
	Ext       string             `json:"ext"`
	Path      string             `json:"path"`
	Items     []*TreeSourceModel `json:"items"`
}

func (a *Application) GetCommand(cmdName string) (bool, string) {
	cmdString := ""

	for _, raw := range a.Command.([]interface{}) {
		each := raw.(map[string]interface{})

		if each["key"].(string) == cmdName {
			cmdString = each["value"].(string)
			break
		}
	}

	for _, raw := range a.Variable.([]interface{}) {
		each := raw.(map[string]interface{})
		variable := fmt.Sprintf("$%s", each["key"].(string))

		if strings.Contains(cmdString, variable) {
			cmdString = strings.Replace(cmdString, variable, each["value"].(string), -1)
		}
	}

	if cmdString == "" {
		return false, ""
	}

	return true, cmdString
}

func (a *Application) GetAllInternalApps() ([]Application, error) {
	cursor, err := Find(a, dbox.Eq("IsInternalApp", true))
	if err != nil {
		return nil, err
	}

	data := make([]Application, 0)
	err = cursor.Fetch(&data, 0, true)
	if err != nil {
		return nil, err
	}

	return data, nil
}

func (a *Application) UpdateDeployedInfo(serverID string, mode string) error {
	cache := make(map[string]bool, 0)
	deployedTo := make([]string, 0)

	for _, each := range a.DeployedTo {
		if _, ok := cache[each]; ok {
			continue
		}

		if each == serverID {
			continue
		}

		cache[each] = true
		deployedTo = append(deployedTo, each)
	}

	if mode == "add" {
		deployedTo = append(deployedTo, serverID)
	}

	a.DeployedTo = deployedTo
	return Save(a)
}

func (a *Application) runWithTimeout(sshSetting sshclient.SshSetting, cmd string) (string, error) {
	timeout := time.Second * time.Duration(5)
	cRunCommand := make(chan string, 1)
	var res string

	go func(res *string) {
		temp, err := sshSetting.RunCommandSshAsMap(cmd)
		*res = temp[0].Output

		if err != nil {
			cRunCommand <- err.Error()
		} else {
			cRunCommand <- ""
		}
	}(&res)

	errorMessage := ""
	select {
	case receiveRunCommandOutput := <-cRunCommand:
		errorMessage = receiveRunCommandOutput
	case <-time.After(time.Second * time.Duration(timeout)):
		errorMessage = ""
	}

	if strings.TrimSpace(errorMessage) != "" {
		return "", errors.New(errorMessage)
	}

	return strings.TrimSpace(res), nil
}

func (a *Application) RunApp(server *Server) (bool, error) {
	sshSetting, _, err := server.Connect()
	if err != nil {
		return false, err
	}

	var cmdRun, cmdGetPID string

	if a.IsInternalApp {
		_, cmdRun = a.GetCommand(App_Command_Start)
	} else {
		cmdRun = fmt.Sprintf("cd $EC_APP_PATH/web && chmod 755 %s && ./%s &", a.ID, a.ID)
	}

	fmt.Println("cmd start", cmdRun)

	output, err := a.runWithTimeout(sshSetting, cmdRun)
	if err != nil {
		return false, err
	}

	if a.IsInternalApp {
		_, cmdGetPID = a.GetCommand(App_Command_RunningStatus)
	} else {
		cmdGetPID = fmt.Sprintf("pidof %s", a.ID)
	}

	output, err = a.runWithTimeout(sshSetting, cmdGetPID)
	if err != nil {
		return false, err
	}

	isRun := (strings.TrimSpace(output) != "")
	return isRun, nil
}

func (a *Application) StopApp(server *Server) (bool, error) {
	sshSetting, _, err := server.Connect()
	if err != nil {
		return false, err
	}

	var cmdStop, cmdGetPID string

	if a.IsInternalApp {
		_, cmdStop = a.GetCommand(App_Command_Stop)
	} else {
		cmdStop = fmt.Sprintf("sudo kill -9 `pidof %s` %s", a.ID)
	}

	fmt.Println("cmd stop", cmdStop)

	output, err := a.runWithTimeout(sshSetting, cmdStop)
	if err != nil {
		return false, err
	}

	if a.IsInternalApp {
		_, cmdGetPID = a.GetCommand(App_Command_RunningStatus)
	} else {
		cmdGetPID = fmt.Sprintf("pidof %s", a.ID)
	}

	output, err = a.runWithTimeout(sshSetting, cmdGetPID)
	if err != nil {
		return false, err
	}

	isRun := (strings.TrimSpace(output) != "")
	return isRun, nil
}
<|endoftext|>


<commit_before>package parser

import (
  "goprotobuf.googlecode.com/hg/proto"
  ir "tritium/proto"
  "io/ioutil"
  . "tritium/tokenizer" // was meant to be in this package
  "path"
  //"fmt"
)

type Parser struct {
  *Tokenizer
  FileName string
  DirName string
  FullPath string
  Lookahead *Token
  counter int
  header bool
}

func (p *Parser) gensym() string {
  p.counter++
  return p.FullPath + string(p.counter)
}

func (p *Parser) peek() *Token {
  return p.Lookahead
}

func (p *Parser) pop() *Token {
  val := p.Lookahead
  p.Lookahead = p.Tokenizer.Pop()
  return val
}

func MakeParser(fullpath string) *Parser {
  src, _ := ioutil.ReadFile(fullpath)
  d, f := path.Split(fullpath)
  p := &Parser {
    Tokenizer: MakeTokenizer(src),
    FileName: f,
    DirName: d,
    FullPath: fullpath,
    Lookahead: nil,
    counter: 0,
  }
  p.pop()
  return p
}

func (p *Parser) Parse() *ir.ScriptObject {
  script := new(ir.ScriptObject)
  script.Name = proto.String(p.FullPath)

  stmts := make([]*ir.Instruction, 0)
  defs := make([]*ir.Function, 0)

  switch p.peek().Lexeme {
  case FUNC:
    for p.peek().Lexeme != EOF {
      defs = append(defs, p.definition())
    }
    if len(defs) == 0 {
      defs = nil
    }
    script.Functions = defs
  default:
    for p.peek().Lexeme != EOF {
      stmts = append(stmts, p.statement())
    }
    if len(stmts) == 0 {
      stmts = nil
    }
    script.Root = &ir.Instruction {
      Type: ir.NewInstruction_InstructionType(ir.Instruction_BLOCK),
      Children: stmts,
    }
  }
  return script
}

func (p *Parser) statement() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case IMPORT:
    token := p.pop() // pop the "@import" keyword
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_IMPORT)
    node.Value = proto.String(path.Join(p.DirName, token.Value))
  default:
    node = p.expression()
  }
  return node
}

func (p *Parser) expression() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case STRING, REGEXP, POS:
    node = p.literal()
  case READ:
    node = p.read()
  case ID:
    node = p.call()
  case GVAR, LVAR:
    node = p.variable()
  default:
    panic("malformed expression")
  }
  return node
}

func (p *Parser) literal() *ir.Instruction {
  node := new(ir.Instruction)
  token := p.pop()
  switch token.Lexeme {
  case STRING:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    node.Value = proto.String(token.Value)
  case REGEXP:
    pattern := new(ir.Instruction)
    options := new(ir.Instruction)
    po := make([]*ir.Instruction, 2)
    pattern.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    pattern.Value = proto.String(token.Value)
    options.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    options.Value = proto.String(token.ExtraValue)
    po[0] = pattern
    po[1] = options
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String("regexp")
    node.Arguments = po
  case POS:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_POSITION)
    node.Value = proto.String(token.Value)
  }
  return node
}

func (p *Parser) read() *ir.Instruction {
  node := new(ir.Instruction)
  p.pop() // pop the "read" keyword
  if p.peek().Lexeme != LPAREN {
    panic("argument list expected for read")
  }
  p.pop() // pop the lparen
  if p.peek().Lexeme != STRING {
    panic("read requires a literal string argument")
  }
  readPath := p.pop().Value
  if p.peek().Lexeme != RPAREN {
    panic("unterminated argument list in read")
  }
  p.pop() // pop the rparen
  contents, _ := ioutil.ReadFile(path.Join(p.DirName, readPath))
  node.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
  node.Value = proto.String(string(contents))
  return node
}

func (p *Parser) call() *ir.Instruction {
  node := new(ir.Instruction)
  funcName := p.pop().Value // grab the function name
  if p.peek().Lexeme != LPAREN {
    panic("argument list expected for call to " + funcName)
  }
  p.pop() // pop the lparen
  ords, kwds := p.arguments() // gather the arguments
  if p.peek().Lexeme != RPAREN {
    panic("unterminated argument list in call to " + funcName)
  }
  p.pop() // pop the rparen
  var block []*ir.Instruction
  if p.peek().Lexeme == LBRACE {
    block = p.block()
  }

  // Expand keyword args
  if kwds != nil {
    kwdToGensym := make(map[string]string, len(kwds))
    outer := make([]*ir.Instruction, 0)
    for k, v:= range kwds {
      temp := new(ir.Instruction)
      temp.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      temp.Value = proto.String("var")
      temp.Arguments = make([]*ir.Instruction, 2)
      tempName := p.gensym()
      temp.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(tempName),
      }
      temp.Arguments[1] = v
      outer = append(outer, temp)
      kwdToGensym[k] = tempName
    }
    inner := make([]*ir.Instruction, 0)
    for k, _ := range kwds {
      getter := new(ir.Instruction)
      getter.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      getter.Value = proto.String("var")
      getter.Arguments = make([]*ir.Instruction, 1)
      getter.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(kwdToGensym[k]),
      }
      setter := new(ir.Instruction)
      setter.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      setter.Value = proto.String("set")
      setter.Arguments = make([]*ir.Instruction, 2)
      setter.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(k),
      }
      setter.Arguments[1] = getter
      inner = append(inner, setter)
    }

    theCall := new(ir.Instruction)
    theCall.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    theCall.Value = proto.String(funcName)
    theCall.Arguments = ords

    if block == nil {
      block = inner
    } else {
      for _, v := range block {
        inner = append(inner, v)
      }
    }

    theCall.Children = inner
    outer = append(outer, theCall)

    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_BLOCK)
    node.Children = outer

  } else {
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String(funcName)
    node.Arguments = ords
    node.Children = block
  }
  return node
}

func (p *Parser) arguments() (ords []*ir.Instruction, kwds map[string]*ir.Instruction) {
  ords, kwds = make([]*ir.Instruction, 0), make(map[string]*ir.Instruction, 0)
  if p.peek().Lexeme != RPAREN {
    if p.peek().Lexeme == KWD {
      k := p.pop().Value
      kwds[k] = p.expression()
    } else {
      ords = append(ords, p.expression())
    }
  }
  for p.peek().Lexeme != RPAREN {
    if p.peek().Lexeme == COMMA {
      p.pop()
    } else {
      panic("arguments must be separated by commas")
    }
    if p.peek().Lexeme == KWD {
      k := p.pop().Value
      kwds[k] = p.expression()
    } else {
      ords = append(ords, p.expression())
    }
  }
  if len(ords) == 0 {
    ords = nil
  }
  if len(kwds) == 0 {
    kwds = nil
  }
  return ords, kwds
}

func (p *Parser) variable() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case LVAR:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_LOCAL_VAR)
    node.Value = proto.String(p.pop().Value)
    if p.peek().Lexeme == EQUAL {
      p.pop() // pop the equal sign
      node.Arguments = make([]*ir.Instruction, 1)
      node.Arguments[0] = p.expression()
    }
  case GVAR:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String("var")
    name := p.pop().Value
    value := new(ir.Instruction)
    if p.peek().Lexeme == EQUAL {
      p.pop() // pop the equal sign
      value = p.expression()
    } else {
      value = nil
    }
    if value == nil {
      node.Arguments = make([]*ir.Instruction, 1)
    } else {
      node.Arguments = make([]*ir.Instruction, 2)
      node.Arguments[1] = value
    }
    node.Arguments[0] = &ir.Instruction {
      Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
      Value: proto.String(name),
    }
  }
  if p.peek().Lexeme == LBRACE {
    node.Children = p.block()
  }
  return node
}

func (p *Parser) block() []*ir.Instruction {
  stmts := make([]*ir.Instruction, 0)
  p.pop() // pop the lbrace
  for p.peek().Lexeme != RBRACE {
    stmts = append(stmts, p.statement())
  }
  p.pop() // pop the rbrace
  if len(stmts) == 0 {
    stmts = nil
  }
  return stmts
}

func (p *Parser) definition() *ir.Function {
  isSignature := false
  node := new(ir.Function)

  p.pop() // pop the "@func" keyword
  contextType := ""
  if p.peek().Lexeme == TYPE {
    contextType = p.pop().Value
    if p.peek().Lexeme != DOT {
      panic("function context and function name must be separated by '.'")
    }
    p.pop() // pop the dot
  }

  if p.peek().Lexeme != ID {
    panic("invalid function name in definition")
  }
  funcName := p.pop().Value

  if p.peek().Lexeme != LPAREN {
    panic("malformed parameter list in function signature")
  }
  p.pop() // pop the lparen
  params := p.parameters()
  if len(params) == 0 {
    params = nil
  }
  p.pop() // pop the rparen

  returnType := ""
  opensType := ""
  if p.peek().Lexeme == TYPE {
    isSignature = true
    returnType = p.pop().Value
    if p.peek().Lexeme == TYPE {
      opensType = p.pop().Value
    }
  }

  node.Name = proto.String(funcName)
  node.Args = params
  node.ScopeType = proto.String(contextType)
  node.ReturnType = proto.String(returnType)
  node.OpensType = proto.String(opensType)

  if isSignature {
    if p.peek().Lexeme == LBRACE {
      panic("body not permitted in function signature")
    }
    return node
  }

  if p.peek().Lexeme != LBRACE {
    panic("function definition is missing a body")
  }
  funcBody := &ir.Instruction {
    Type: ir.NewInstruction_InstructionType(ir.Instruction_BLOCK),
    Children: p.block(),
  }
  node.Instruction = funcBody
  return node
}

func (p *Parser) parameters() []*ir.Function_Argument {
  params := make([]*ir.Function_Argument, 0)
  // if p.peek().Lexeme != RPAREN {
  //   if p.peek().Lexeme != TYPE {
  //     panic("function parameter is missing a type")
  //   }
  //   param := &ir.Function_Argument {
  //     TypeString: proto.String(p.pop().Value),
  //   }
  //   if p.peek().Lexeme != LVAR {
  //     panic("function parameter has invalid name")
  //   }
  //   param.Name = proto.String(p.pop().Value)
  //   params = append(params, param)
  // }
  counter := 0
  for p.peek().Lexeme != RPAREN {
    if counter > 0 {
      if p.peek().Lexeme != COMMA {
        panic("parameter list must be separated by commas")
      }
      p.pop() // pop the comma
    }
    if p.peek().Lexeme != TYPE {
      panic("function parameter is missing a type")
    }
    param := &ir.Function_Argument {
      TypeString: proto.String(p.pop().Value),
    }
    if p.peek().Lexeme != LVAR {
      panic("function parameter has invalid name")
    }
    param.Name = proto.String(p.pop().Value)
    params = append(params, param)
    counter++
  }
  return params
}

<commit_msg>Cleaning up comma parsing in the argument-list parsing function.<commit_after>package parser

import (
  "goprotobuf.googlecode.com/hg/proto"
  ir "tritium/proto"
  "io/ioutil"
  . "tritium/tokenizer" // was meant to be in this package
  "path"
  //"fmt"
)

type Parser struct {
  *Tokenizer
  FileName string
  DirName string
  FullPath string
  Lookahead *Token
  counter int
  header bool
}

func (p *Parser) gensym() string {
  p.counter++
  return p.FullPath + string(p.counter)
}

func (p *Parser) peek() *Token {
  return p.Lookahead
}

func (p *Parser) pop() *Token {
  val := p.Lookahead
  p.Lookahead = p.Tokenizer.Pop()
  return val
}

func MakeParser(fullpath string) *Parser {
  src, _ := ioutil.ReadFile(fullpath)
  d, f := path.Split(fullpath)
  p := &Parser {
    Tokenizer: MakeTokenizer(src),
    FileName: f,
    DirName: d,
    FullPath: fullpath,
    Lookahead: nil,
    counter: 0,
  }
  p.pop()
  return p
}

func (p *Parser) Parse() *ir.ScriptObject {
  script := new(ir.ScriptObject)
  script.Name = proto.String(p.FullPath)

  stmts := make([]*ir.Instruction, 0)
  defs := make([]*ir.Function, 0)

  switch p.peek().Lexeme {
  case FUNC:
    for p.peek().Lexeme != EOF {
      defs = append(defs, p.definition())
    }
    if len(defs) == 0 {
      defs = nil
    }
    script.Functions = defs
  default:
    for p.peek().Lexeme != EOF {
      stmts = append(stmts, p.statement())
    }
    if len(stmts) == 0 {
      stmts = nil
    }
    script.Root = &ir.Instruction {
      Type: ir.NewInstruction_InstructionType(ir.Instruction_BLOCK),
      Children: stmts,
    }
  }
  return script
}

func (p *Parser) statement() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case IMPORT:
    token := p.pop() // pop the "@import" keyword
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_IMPORT)
    node.Value = proto.String(path.Join(p.DirName, token.Value))
  default:
    node = p.expression()
  }
  return node
}

func (p *Parser) expression() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case STRING, REGEXP, POS:
    node = p.literal()
  case READ:
    node = p.read()
  case ID:
    node = p.call()
  case GVAR, LVAR:
    node = p.variable()
  default:
    panic("malformed expression")
  }
  return node
}

func (p *Parser) literal() *ir.Instruction {
  node := new(ir.Instruction)
  token := p.pop()
  switch token.Lexeme {
  case STRING:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    node.Value = proto.String(token.Value)
  case REGEXP:
    pattern := new(ir.Instruction)
    options := new(ir.Instruction)
    po := make([]*ir.Instruction, 2)
    pattern.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    pattern.Value = proto.String(token.Value)
    options.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
    options.Value = proto.String(token.ExtraValue)
    po[0] = pattern
    po[1] = options
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String("regexp")
    node.Arguments = po
  case POS:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_POSITION)
    node.Value = proto.String(token.Value)
  }
  return node
}

func (p *Parser) read() *ir.Instruction {
  node := new(ir.Instruction)
  p.pop() // pop the "read" keyword
  if p.peek().Lexeme != LPAREN {
    panic("argument list expected for read")
  }
  p.pop() // pop the lparen
  if p.peek().Lexeme != STRING {
    panic("read requires a literal string argument")
  }
  readPath := p.pop().Value
  if p.peek().Lexeme != RPAREN {
    panic("unterminated argument list in read")
  }
  p.pop() // pop the rparen
  contents, _ := ioutil.ReadFile(path.Join(p.DirName, readPath))
  node.Type = ir.NewInstruction_InstructionType(ir.Instruction_TEXT)
  node.Value = proto.String(string(contents))
  return node
}

func (p *Parser) call() *ir.Instruction {
  node := new(ir.Instruction)
  funcName := p.pop().Value // grab the function name
  if p.peek().Lexeme != LPAREN {
    panic("argument list expected for call to " + funcName)
  }
  p.pop() // pop the lparen
  ords, kwds := p.arguments() // gather the arguments
  if p.peek().Lexeme != RPAREN {
    panic("unterminated argument list in call to " + funcName)
  }
  p.pop() // pop the rparen
  var block []*ir.Instruction
  if p.peek().Lexeme == LBRACE {
    block = p.block()
  }

  // Expand keyword args
  if kwds != nil {
    kwdToGensym := make(map[string]string, len(kwds))
    outer := make([]*ir.Instruction, 0)
    for k, v:= range kwds {
      temp := new(ir.Instruction)
      temp.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      temp.Value = proto.String("var")
      temp.Arguments = make([]*ir.Instruction, 2)
      tempName := p.gensym()
      temp.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(tempName),
      }
      temp.Arguments[1] = v
      outer = append(outer, temp)
      kwdToGensym[k] = tempName
    }
    inner := make([]*ir.Instruction, 0)
    for k, _ := range kwds {
      getter := new(ir.Instruction)
      getter.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      getter.Value = proto.String("var")
      getter.Arguments = make([]*ir.Instruction, 1)
      getter.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(kwdToGensym[k]),
      }
      setter := new(ir.Instruction)
      setter.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
      setter.Value = proto.String("set")
      setter.Arguments = make([]*ir.Instruction, 2)
      setter.Arguments[0] = &ir.Instruction {
        Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
        Value: proto.String(k),
      }
      setter.Arguments[1] = getter
      inner = append(inner, setter)
    }

    theCall := new(ir.Instruction)
    theCall.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    theCall.Value = proto.String(funcName)
    theCall.Arguments = ords

    if block == nil {
      block = inner
    } else {
      for _, v := range block {
        inner = append(inner, v)
      }
    }

    theCall.Children = inner
    outer = append(outer, theCall)

    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_BLOCK)
    node.Children = outer

  } else {
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String(funcName)
    node.Arguments = ords
    node.Children = block
  }
  return node
}

func (p *Parser) arguments() (ords []*ir.Instruction, kwds map[string]*ir.Instruction) {
  ords, kwds = make([]*ir.Instruction, 0), make(map[string]*ir.Instruction, 0)
  counter := 0
  for p.peek().Lexeme != RPAREN {
    if counter > 0 {
      if p.peek().Lexeme != COMMA {
        panic("arguments must be separated by commas")
      }
      p.pop() // pop the comma
    }
    if p.peek().Lexeme == KWD {
      k := p.pop().Value
      kwds[k] = p.expression()
    } else {
      ords = append(ords, p.expression())
    }
    counter++
  }
  if len(ords) == 0 {
    ords = nil
  }
  if len(kwds) == 0 {
    kwds = nil
  }
  return ords, kwds
}

func (p *Parser) variable() *ir.Instruction {
  node := new(ir.Instruction)
  switch p.peek().Lexeme {
  case LVAR:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_LOCAL_VAR)
    node.Value = proto.String(p.pop().Value)
    if p.peek().Lexeme == EQUAL {
      p.pop() // pop the equal sign
      node.Arguments = make([]*ir.Instruction, 1)
      node.Arguments[0] = p.expression()
    }
  case GVAR:
    node.Type = ir.NewInstruction_InstructionType(ir.Instruction_FUNCTION_CALL)
    node.Value = proto.String("var")
    name := p.pop().Value
    value := new(ir.Instruction)
    if p.peek().Lexeme == EQUAL {
      p.pop() // pop the equal sign
      value = p.expression()
    } else {
      value = nil
    }
    if value == nil {
      node.Arguments = make([]*ir.Instruction, 1)
    } else {
      node.Arguments = make([]*ir.Instruction, 2)
      node.Arguments[1] = value
    }
    node.Arguments[0] = &ir.Instruction {
      Type: ir.NewInstruction_InstructionType(ir.Instruction_TEXT),
      Value: proto.String(name),
    }
  }
  if p.peek().Lexeme == LBRACE {
    node.Children = p.block()
  }
  return node
}

func (p *Parser) block() []*ir.Instruction {
  stmts := make([]*ir.Instruction, 0)
  p.pop() // pop the lbrace
  for p.peek().Lexeme != RBRACE {
    stmts = append(stmts, p.statement())
  }
  p.pop() // pop the rbrace
  if len(stmts) == 0 {
    stmts = nil
  }
  return stmts
}

func (p *Parser) definition() *ir.Function {
  isSignature := false
  node := new(ir.Function)

  p.pop() // pop the "@func" keyword
  contextType := ""
  if p.peek().Lexeme == TYPE {
    contextType = p.pop().Value
    if p.peek().Lexeme != DOT {
      panic("function context and function name must be separated by '.'")
    }
    p.pop() // pop the dot
  }

  if p.peek().Lexeme != ID {
    panic("invalid function name in definition")
  }
  funcName := p.pop().Value

  if p.peek().Lexeme != LPAREN {
    panic("malformed parameter list in function signature")
  }
  p.pop() // pop the lparen
  params := p.parameters()
  if len(params) == 0 {
    params = nil
  }
  p.pop() // pop the rparen

  returnType := ""
  opensType := ""
  if p.peek().Lexeme == TYPE {
    isSignature = true
    returnType = p.pop().Value
    if p.peek().Lexeme == TYPE {
      opensType = p.pop().Value
    }
  }

  node.Name = proto.String(funcName)
  node.Args = params
  node.ScopeType = proto.String(contextType)
  node.ReturnType = proto.String(returnType)
  node.OpensType = proto.String(opensType)

  if isSignature {
    if p.peek().Lexeme == LBRACE {
      panic("body not permitted in function signature")
    }
    return node
  }

  if p.peek().Lexeme != LBRACE {
    panic("function definition is missing a body")
  }
  funcBody := &ir.Instruction {
    Type: ir.NewInstruction_InstructionType(ir.Instruction_BLOCK),
    Children: p.block(),
  }
  node.Instruction = funcBody
  return node
}

func (p *Parser) parameters() []*ir.Function_Argument {
  params := make([]*ir.Function_Argument, 0)
  counter := 0
  for p.peek().Lexeme != RPAREN {
    if counter > 0 {
      if p.peek().Lexeme != COMMA {
        panic("parameter list must be separated by commas")
      }
      p.pop() // pop the comma
    }
    if p.peek().Lexeme != TYPE {
      panic("function parameter is missing a type")
    }
    param := &ir.Function_Argument {
      TypeString: proto.String(p.pop().Value),
    }
    if p.peek().Lexeme != LVAR {
      panic("function parameter has invalid name")
    }
    param.Name = proto.String(p.pop().Value)
    params = append(params, param)
    counter++
  }
  return params
}

<|endoftext|>


<commit_before>package parser

import (
	"errors"
	"fmt"
	//"go/ast"
	"regexp"
	"strconv"
	"strings"
)

type Operation struct {
	HttpMethod       string            `json:"httpMethod"`
	Nickname         string            `json:"nickname"`
	Type             string            `json:"type"`
	Items            OperationItems    `json:"items,omitempty"`
	Summary          string            `json:"summary,omitempty"`
	Notes            string            `json:"notes,omitempty"`
	Parameters       []Parameter       `json:"parameters,omitempty"`
	ResponseMessages []ResponseMessage `json:"responseMessages,omitempty"`
	Consumes         []string          `json:"-"`
	Produces         []string          `json:"produces,omitempty"`
	Authorizations   []Authorization   `json:"authorizations,omitempty"`
	Protocols        []Protocol        `json:"protocols,omitempty"`
	Path             string            `json:"-"`
	ForceResource    string            `json:"-"`
	parser           *Parser
	Models           []*Model `json:"-"`
	packageName      string
}
type OperationItems struct {
	Ref  string `json:"$ref,omitempty"`
	Type string `json:"type,omitempty"`
}

func NewOperation(p *Parser, packageName string) *Operation {
	return &Operation{
		parser:      p,
		Models:      make([]*Model, 0),
		packageName: packageName,
	}
}

func (operation *Operation) SetItemsType(itemsType string) {
	operation.Items = OperationItems{}
	if IsBasicType(itemsType) {
		operation.Items.Type = itemsType
	} else {
		operation.Items.Ref = itemsType
	}
}

func (operation *Operation) ParseComment(comment string) error {
	commentLine := strings.TrimSpace(strings.TrimLeft(comment, "//"))
	attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
	switch attribute {
	case "@router":
		if err := operation.ParseRouterComment(commentLine); err != nil {
			return err
		}
	case "@resource":
		resource := strings.TrimSpace(commentLine[len("@Resource"):])
		if resource[0:1] == "/" {
			resource = resource[1:]
		}
		operation.ForceResource = resource
	case "@title":
		operation.Nickname = strings.TrimSpace(commentLine[len("@Title"):])
	case "@description":
		operation.Summary = strings.TrimSpace(commentLine[len("@Description"):])
	case "@success":
		sourceString := strings.TrimSpace(commentLine[len("@Success"):])
		if err := operation.ParseResponseComment(sourceString); err != nil {
			return err
		}
	case "@param":
		if err := operation.ParseParamComment(commentLine); err != nil {
			return err
		}
	case "@failure":
		sourceString := strings.TrimSpace(commentLine[len("@Failure"):])
		if err := operation.ParseResponseComment(sourceString); err != nil {
			return err
		}
	case "@accept":
		if err := operation.ParseAcceptComment(commentLine); err != nil {
			return err
		}
	}

	operation.Models = operation.getUniqueModels()

	return nil
}

func (operation *Operation) getUniqueModels() []*Model {

	uniqueModels := make([]*Model, 0, len(operation.Models))
	modelIds := map[string]bool{}

	for _, model := range operation.Models {
		if _, exists := modelIds[model.Id]; exists {
			continue
		}
		uniqueModels = append(uniqueModels, model)
		modelIds[model.Id] = true
	}

	return uniqueModels
}

func (operation *Operation) registerType(typeName string) (string, error) {
	registerType := ""

	if translation, ok := typeDefTranslations[typeName]; ok {
		registerType = translation
	} else if IsBasicType(typeName) {
		registerType = typeName
	} else {
		model := NewModel(operation.parser)
		knownModelNames := map[string]bool{}

		err, innerModels := model.ParseModel(typeName, operation.parser.CurrentPackage, knownModelNames)
		if err != nil {
			return registerType, err
		}
		if translation, ok := typeDefTranslations[typeName]; ok {
			registerType = translation
		} else {
			registerType = model.Id

			operation.Models = append(operation.Models, model)
			operation.Models = append(operation.Models, innerModels...)
		}
	}

	return registerType, nil
}

// Parse params return []string of param properties
// @Param	queryText		form	      string	  true		        "The email for login"
// 			[param name]    [param type] [data type]  [is mandatory?]   [Comment]
func (operation *Operation) ParseParamComment(commentLine string) error {
	swaggerParameter := Parameter{}
	paramString := strings.TrimSpace(commentLine[len("@Param "):])

	re := regexp.MustCompile(`([\w]+)[\s]+([\w]+)[\s]+([\w.]+)[\s]+([\w]+)[\s]+"([^"]+)"`)

	if matches := re.FindStringSubmatch(paramString); len(matches) != 6 {
		return fmt.Errorf("Can not parse param comment \"%s\", skipped.", paramString)
	} else {
		typeName, err := operation.registerType(matches[3])
		if err != nil {
			return err
		}

		swaggerParameter.Name = matches[1]
		swaggerParameter.ParamType = matches[2]
		swaggerParameter.Type = typeName
		swaggerParameter.DataType = typeName
		swaggerParameter.Required = strings.ToLower(matches[4]) == "true"
		swaggerParameter.Description = matches[5]

		operation.Parameters = append(operation.Parameters, swaggerParameter)
	}

	return nil
}

// @Accept  json
func (operation *Operation) ParseAcceptComment(commentLine string) error {
	accepts := strings.Split(strings.TrimSpace(strings.TrimSpace(commentLine[len("@Accept"):])), ",")
	for _, a := range accepts {
		switch a {
		case "json", "application/json":
			operation.Consumes = append(operation.Consumes, ContentTypeJson)
			operation.Produces = append(operation.Produces, ContentTypeJson)
		case "xml", "text/xml":
			operation.Consumes = append(operation.Consumes, ContentTypeXml)
			operation.Produces = append(operation.Produces, ContentTypeXml)
		case "plain", "text/plain":
			operation.Consumes = append(operation.Consumes, ContentTypePlain)
			operation.Produces = append(operation.Produces, ContentTypePlain)
		case "html", "text/html":
			operation.Consumes = append(operation.Consumes, ContentTypeHtml)
			operation.Produces = append(operation.Produces, ContentTypeHtml)
		}
	}
	return nil
}

// @Router /customer/get-wishlist/{wishlist_id} [get]
func (operation *Operation) ParseRouterComment(commentLine string) error {
	sourceString := strings.TrimSpace(commentLine[len("@Router"):])

	re := regexp.MustCompile(`([\w\.\/\-{}]+)[^\[]+\[([^\]]+)`)
	var matches []string

	if matches = re.FindStringSubmatch(sourceString); len(matches) != 3 {
		return fmt.Errorf("Can not parse router comment \"%s\", skipped.", commentLine)
	}

	operation.Path = matches[1]
	operation.HttpMethod = strings.ToUpper(matches[2])
	return nil
}

// @Success 200 {object} model.OrderRow "Error message, if code != 200"
func (operation *Operation) ParseResponseComment(commentLine string) error {
	re := regexp.MustCompile(`([\d]+)[\s]+([\w\{\}]+)[\s]+([\w\-\.\/]+)[^"]*(.*)?`)
	var matches []string

	if matches = re.FindStringSubmatch(commentLine); len(matches) != 5 {
		return fmt.Errorf("Can not parse response comment \"%s\", skipped.", commentLine)
	}

	response := ResponseMessage{}
	if code, err := strconv.Atoi(matches[1]); err != nil {
		return errors.New("Success http code must be int")
	} else {
		response.Code = code
	}
	response.Message = strings.Trim(matches[4], "\"")

	typeName, err := operation.registerType(matches[3])
	if err != nil {
		return err
	}

	response.ResponseModel = typeName
	if response.Code == 200 {
		if matches[2] == "{array}" {
			operation.SetItemsType(typeName)
			operation.Type = "array"
		} else {
			operation.Type = typeName
		}
	}

	operation.ResponseMessages = append(operation.ResponseMessages, response)
	return nil
}
<commit_msg>changed Split to Fields so we can eat tabs, too.<commit_after>package parser

import (
	"errors"
	"fmt"
	//"go/ast"
	"log"
	"regexp"
	"strconv"
	"strings"
)

type Operation struct {
	HttpMethod       string            `json:"httpMethod"`
	Nickname         string            `json:"nickname"`
	Type             string            `json:"type"`
	Items            OperationItems    `json:"items,omitempty"`
	Summary          string            `json:"summary,omitempty"`
	Notes            string            `json:"notes,omitempty"`
	Parameters       []Parameter       `json:"parameters,omitempty"`
	ResponseMessages []ResponseMessage `json:"responseMessages,omitempty"`
	Consumes         []string          `json:"-"`
	Produces         []string          `json:"produces,omitempty"`
	Authorizations   []Authorization   `json:"authorizations,omitempty"`
	Protocols        []Protocol        `json:"protocols,omitempty"`
	Path             string            `json:"-"`
	ForceResource    string            `json:"-"`
	parser           *Parser
	Models           []*Model `json:"-"`
	packageName      string
}
type OperationItems struct {
	Ref  string `json:"$ref,omitempty"`
	Type string `json:"type,omitempty"`
}

func NewOperation(p *Parser, packageName string) *Operation {
	return &Operation{
		parser:      p,
		Models:      make([]*Model, 0),
		packageName: packageName,
	}
}

func (operation *Operation) SetItemsType(itemsType string) {
	operation.Items = OperationItems{}
	if IsBasicType(itemsType) {
		operation.Items.Type = itemsType
	} else {
		operation.Items.Ref = itemsType
	}
}

func (operation *Operation) ParseComment(comment string) error {
	commentLine := strings.TrimSpace(strings.TrimLeft(comment, "//"))
	attribute := strings.ToLower(strings.Fields(commentLine)[0])
	switch attribute {
	case "@router":
		if err := operation.ParseRouterComment(commentLine); err != nil {
			return err
		}
	case "@resource":
		resource := strings.TrimSpace(commentLine[len("@Resource"):])
		if resource[0:1] == "/" {
			resource = resource[1:]
		}
		operation.ForceResource = resource
	case "@title":
		operation.Nickname = strings.TrimSpace(commentLine[len("@Title"):])
	case "@description":
		operation.Summary = strings.TrimSpace(commentLine[len("@Description"):])
	case "@success":
		sourceString := strings.TrimSpace(commentLine[len("@Success"):])
		if err := operation.ParseResponseComment(sourceString); err != nil {
			return err
		}
	case "@param":
		if err := operation.ParseParamComment(commentLine); err != nil {
			return err
		}
	case "@failure":
		sourceString := strings.TrimSpace(commentLine[len("@Failure"):])
		if err := operation.ParseResponseComment(sourceString); err != nil {
			return err
		}
	case "@accept":
		if err := operation.ParseAcceptComment(commentLine); err != nil {
			return err
		}
	}

	operation.Models = operation.getUniqueModels()

	return nil
}

func (operation *Operation) getUniqueModels() []*Model {

	uniqueModels := make([]*Model, 0, len(operation.Models))
	modelIds := map[string]bool{}

	for _, model := range operation.Models {
		if _, exists := modelIds[model.Id]; exists {
			continue
		}
		uniqueModels = append(uniqueModels, model)
		modelIds[model.Id] = true
	}

	return uniqueModels
}

func (operation *Operation) registerType(typeName string) (string, error) {
	registerType := ""

	if translation, ok := typeDefTranslations[typeName]; ok {
		registerType = translation
	} else if IsBasicType(typeName) {
		registerType = typeName
	} else {
		model := NewModel(operation.parser)
		knownModelNames := map[string]bool{}

		err, innerModels := model.ParseModel(typeName, operation.parser.CurrentPackage, knownModelNames)
		if err != nil {
			return registerType, err
		}
		if translation, ok := typeDefTranslations[typeName]; ok {
			registerType = translation
		} else {
			registerType = model.Id

			operation.Models = append(operation.Models, model)
			operation.Models = append(operation.Models, innerModels...)
		}
	}

	return registerType, nil
}

// Parse params return []string of param properties
// @Param	queryText		form	      string	  true		        "The email for login"
// 			[param name]    [param type] [data type]  [is mandatory?]   [Comment]
func (operation *Operation) ParseParamComment(commentLine string) error {
	swaggerParameter := Parameter{}
	paramString := strings.TrimSpace(commentLine[len("@Param "):])

	re := regexp.MustCompile(`([\w]+)[\s]+([\w]+)[\s]+([\w.]+)[\s]+([\w]+)[\s]+"([^"]+)"`)

	if matches := re.FindStringSubmatch(paramString); len(matches) != 6 {
		return fmt.Errorf("Can not parse param comment \"%s\", skipped.", paramString)
	} else {
		typeName, err := operation.registerType(matches[3])
		if err != nil {
			return err
		}

		swaggerParameter.Name = matches[1]
		swaggerParameter.ParamType = matches[2]
		swaggerParameter.Type = typeName
		swaggerParameter.DataType = typeName
		swaggerParameter.Required = strings.ToLower(matches[4]) == "true"
		swaggerParameter.Description = matches[5]

		operation.Parameters = append(operation.Parameters, swaggerParameter)
	}

	return nil
}

// @Accept  json
func (operation *Operation) ParseAcceptComment(commentLine string) error {
	accepts := strings.Split(strings.TrimSpace(strings.TrimSpace(commentLine[len("@Accept"):])), ",")
	for _, a := range accepts {
		switch a {
		case "json", "application/json":
			operation.Consumes = append(operation.Consumes, ContentTypeJson)
			operation.Produces = append(operation.Produces, ContentTypeJson)
		case "xml", "text/xml":
			operation.Consumes = append(operation.Consumes, ContentTypeXml)
			operation.Produces = append(operation.Produces, ContentTypeXml)
		case "plain", "text/plain":
			operation.Consumes = append(operation.Consumes, ContentTypePlain)
			operation.Produces = append(operation.Produces, ContentTypePlain)
		case "html", "text/html":
			operation.Consumes = append(operation.Consumes, ContentTypeHtml)
			operation.Produces = append(operation.Produces, ContentTypeHtml)
		}
	}
	return nil
}

// @Router /customer/get-wishlist/{wishlist_id} [get]
func (operation *Operation) ParseRouterComment(commentLine string) error {
	sourceString := strings.TrimSpace(commentLine[len("@Router"):])

	re := regexp.MustCompile(`([\w\.\/\-{}]+)[^\[]+\[([^\]]+)`)
	var matches []string

	if matches = re.FindStringSubmatch(sourceString); len(matches) != 3 {
		return fmt.Errorf("Can not parse router comment \"%s\", skipped.", commentLine)
	}

	operation.Path = matches[1]
	operation.HttpMethod = strings.ToUpper(matches[2])
	return nil
}

// @Success 200 {object} model.OrderRow "Error message, if code != 200"
func (operation *Operation) ParseResponseComment(commentLine string) error {
	re := regexp.MustCompile(`([\d]+)[\s]+([\w\{\}]+)[\s]+([\w\-\.\/]+)[^"]*(.*)?`)
	var matches []string

	if matches = re.FindStringSubmatch(commentLine); len(matches) != 5 {
		return fmt.Errorf("Can not parse response comment \"%s\", skipped.", commentLine)
	}

	response := ResponseMessage{}
	if code, err := strconv.Atoi(matches[1]); err != nil {
		return errors.New("Success http code must be int")
	} else {
		response.Code = code
	}
	response.Message = strings.Trim(matches[4], "\"")

	typeName, err := operation.registerType(matches[3])
	if err != nil {
		return err
	}

	response.ResponseModel = typeName
	if response.Code == 200 {
		if matches[2] == "{array}" {
			operation.SetItemsType(typeName)
			operation.Type = "array"
		} else {
			operation.Type = typeName
		}
	}

	operation.ResponseMessages = append(operation.ResponseMessages, response)
	return nil
}
<|endoftext|>