You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2018/10/12 17:08:28 UTC

[camel-k] branch master updated (72cb20c -> 28ff8dc)

This is an automated email from the ASF dual-hosted git repository.

nferraro pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git.


    from 72cb20c  Merge pull request #176 from lburgazzoli/typed-traits
     new 4caea26  chore(kamel): simplify integration status watcher
     new 2f942e5  chore(kamel) : add a flag to easilly configure logging
     new 28ff8dc  chore(kamel) : add some colours

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Gopkg.lock                                         |  13 ++
 cmd/kamel/kamel.go                                 |   7 +-
 pkg/client/cmd/run.go                              |  89 +++++-----
 pkg/util/util.go                                   |  21 +++
 pkg/util/watch/watch.go                            |  67 +++++++
 .../{json-iterator/go => arsham/blush}/LICENSE     |   2 +-
 vendor/github.com/arsham/blush/blush/blush.go      | 188 ++++++++++++++++++++
 vendor/github.com/arsham/blush/blush/colour.go     | 196 +++++++++++++++++++++
 vendor/github.com/arsham/blush/blush/doc.go        |  30 ++++
 vendor/github.com/arsham/blush/blush/errors.go     |  16 ++
 vendor/github.com/arsham/blush/blush/find.go       | 168 ++++++++++++++++++
 .../arsham/blush/internal/reader/reader.go         | 150 ++++++++++++++++
 .../github.com/arsham/blush/internal/tools/dir.go  | 118 +++++++++++++
 .../arsham/blush/internal/tools/strings.go         |  20 +++
 14 files changed, 1043 insertions(+), 42 deletions(-)
 copy vendor/github.com/{json-iterator/go => arsham/blush}/LICENSE (96%)
 create mode 100644 vendor/github.com/arsham/blush/blush/blush.go
 create mode 100644 vendor/github.com/arsham/blush/blush/colour.go
 create mode 100644 vendor/github.com/arsham/blush/blush/doc.go
 create mode 100644 vendor/github.com/arsham/blush/blush/errors.go
 create mode 100644 vendor/github.com/arsham/blush/blush/find.go
 create mode 100644 vendor/github.com/arsham/blush/internal/reader/reader.go
 create mode 100644 vendor/github.com/arsham/blush/internal/tools/dir.go
 create mode 100644 vendor/github.com/arsham/blush/internal/tools/strings.go


[camel-k] 03/03: chore(kamel) : add some colours

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nferraro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 28ff8dc47511c214dac9cd1d72fa37c3808ce9b1
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Fri Oct 12 16:51:50 2018 +0200

    chore(kamel) : add some colours
---
 Gopkg.lock                                         |  13 ++
 pkg/client/cmd/run.go                              |  31 +++-
 vendor/github.com/arsham/blush/LICENSE             |  21 +++
 vendor/github.com/arsham/blush/blush/blush.go      | 188 ++++++++++++++++++++
 vendor/github.com/arsham/blush/blush/colour.go     | 196 +++++++++++++++++++++
 vendor/github.com/arsham/blush/blush/doc.go        |  30 ++++
 vendor/github.com/arsham/blush/blush/errors.go     |  16 ++
 vendor/github.com/arsham/blush/blush/find.go       | 168 ++++++++++++++++++
 .../arsham/blush/internal/reader/reader.go         | 150 ++++++++++++++++
 .../github.com/arsham/blush/internal/tools/dir.go  | 118 +++++++++++++
 .../arsham/blush/internal/tools/strings.go         |  20 +++
 11 files changed, 943 insertions(+), 8 deletions(-)

diff --git a/Gopkg.lock b/Gopkg.lock
index 0c22817..26413b2 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -26,6 +26,18 @@
   revision = "de5bf2ad457846296e2031421a34e2568e304e35"
 
 [[projects]]
+  digest = "1:69ebf3eaf09a9528d0fa78baecf58a816acd73b6b500eb714c54b9d8a01cff0c"
+  name = "github.com/arsham/blush"
+  packages = [
+    "blush",
+    "internal/reader",
+    "internal/tools",
+  ]
+  pruneopts = "NUT"
+  revision = "a87294e47998d46b608c76cecb35b712103ad45b"
+  version = "v0.5.3"
+
+[[projects]]
   branch = "master"
   digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd"
   name = "github.com/beorn7/perks"
@@ -739,6 +751,7 @@
   analyzer-name = "dep"
   analyzer-version = 1
   input-imports = [
+    "github.com/arsham/blush/blush",
     "github.com/fatih/structs",
     "github.com/mitchellh/mapstructure",
     "github.com/openshift/api/apps/v1",
diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index 39ef9a8..558244e 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -28,6 +28,7 @@ import (
 	"strings"
 
 	"github.com/apache/camel-k/pkg/trait"
+	"github.com/arsham/blush/blush"
 
 	"github.com/apache/camel-k/pkg/util"
 
@@ -193,15 +194,29 @@ func (o *runCmdOptions) waitForIntegrationReady(integration *v1alpha1.Integratio
 func (o *runCmdOptions) printLogs(integration *v1alpha1.Integration) error {
 	scraper := log.NewSelectorScraper(integration.Namespace, "camel.apache.org/integration="+integration.Name)
 	reader := scraper.Start(o.Context)
-	for {
-		str, err := reader.ReadString('\n')
-		if err == io.EOF || o.Context.Err() != nil {
-			break
-		} else if err != nil {
-			return err
-		}
-		fmt.Print(str)
+
+	b := &blush.Blush{
+		Finders: []blush.Finder{
+			blush.NewExact("FATAL", blush.Red),
+			blush.NewExact("ERROR", blush.Red),
+			blush.NewExact("WARN", blush.Yellow),
+			blush.NewExact("INFO", blush.Green),
+			blush.NewExact("DEBUG", blush.Colour{
+				Foreground: blush.RGB{R: 170, G: 170, B: 170},
+				Background: blush.NoRGB,
+			}),
+			blush.NewExact("TRACE", blush.Colour{
+				Foreground: blush.RGB{R: 170, G: 170, B: 170},
+				Background: blush.NoRGB,
+			}),
+		},
+		Reader: ioutil.NopCloser(reader),
+	}
+
+	if _, err := io.Copy(os.Stdout, b); err != nil {
+		fmt.Println(err.Error())
 	}
+
 	return nil
 }
 
diff --git a/vendor/github.com/arsham/blush/LICENSE b/vendor/github.com/arsham/blush/LICENSE
new file mode 100644
index 0000000..03bc557
--- /dev/null
+++ b/vendor/github.com/arsham/blush/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Arsham Shirvani
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/arsham/blush/blush/blush.go b/vendor/github.com/arsham/blush/blush/blush.go
new file mode 100644
index 0000000..1e194a7
--- /dev/null
+++ b/vendor/github.com/arsham/blush/blush/blush.go
@@ -0,0 +1,188 @@
+package blush
+
+import (
+	"bufio"
+	"io"
+
+	"github.com/arsham/blush/internal/reader"
+)
+
+type mode int
+
+const (
+	// Separator string between name of the reader and the contents.
+	Separator = ": "
+
+	// DefaultLineCache is minimum lines to cache.
+	DefaultLineCache = 50
+
+	// DefaultCharCache is minimum characters to cache for each line. This is in
+	// effect only if Read() function is used.
+	DefaultCharCache = 1000
+
+	readMode mode = iota
+	writeToMode
+)
+
+// Blush reads from reader and matches against all finders. If NoCut is true,
+// any unmatched lines are printed as well. If WithFileName is true, blush will
+// write the filename before it writes the output. Read and WriteTo will return
+// ErrReadWriteMix if both Read and WriteTo are called on the same object. See
+// package docs for more details.
+type Blush struct {
+	Finders      []Finder
+	Reader       io.ReadCloser
+	LineCache    uint
+	CharCache    uint
+	NoCut        bool // do not cut out non-matched lines.
+	WithFileName bool
+	closed       bool
+	readLineCh   chan []byte
+	readCh       chan byte
+	mode         mode
+}
+
+// Read creates a goroutine on first invocation to read from the underlying
+// reader. It is considerably slower than WriteTo as it reads the bytes one by
+// one in order to produce the results, therefore you should use WriteTo
+// directly or use io.Copy() on blush.
+func (b *Blush) Read(p []byte) (n int, err error) {
+	if b.closed {
+		return 0, ErrClosed
+	}
+	if b.mode == writeToMode {
+		return 0, ErrReadWriteMix
+	}
+	if b.mode != readMode {
+		if err = b.setup(readMode); err != nil {
+			return 0, err
+		}
+	}
+	for n = 0; n < cap(p); n++ {
+		c, ok := <-b.readCh
+		if !ok {
+			return n, io.EOF
+		}
+		p[n] = c
+	}
+	return n, err
+}
+
+// WriteTo writes matches to w. It returns an error if the writer is nil or
+// there are not paths defined or there is no files found in the Reader.
+func (b *Blush) WriteTo(w io.Writer) (int64, error) {
+	if b.closed {
+		return 0, ErrClosed
+	}
+	if b.mode == readMode {
+		return 0, ErrReadWriteMix
+	}
+	if b.mode != writeToMode {
+		if err := b.setup(writeToMode); err != nil {
+			return 0, err
+		}
+	}
+	var total int
+	if w == nil {
+		return 0, ErrNoWriter
+	}
+	for line := range b.readLineCh {
+		if n, err := w.Write(line); err != nil {
+			return int64(n), err
+		}
+		total += len(line)
+	}
+	return int64(total), nil
+}
+
+func (b *Blush) setup(m mode) error {
+	if b.Reader == nil {
+		return reader.ErrNoReader
+	}
+	if len(b.Finders) < 1 {
+		return ErrNoFinder
+	}
+
+	b.mode = m
+	if b.LineCache == 0 {
+		b.LineCache = DefaultLineCache
+	}
+	if b.CharCache == 0 {
+		b.CharCache = DefaultCharCache
+	}
+	b.readLineCh = make(chan []byte, b.LineCache)
+	b.readCh = make(chan byte, b.CharCache)
+	go b.readLines()
+	if m == readMode {
+		go b.transfer()
+	}
+	return nil
+}
+
+func (b Blush) decorate(input string) (string, bool) {
+	str, ok := lookInto(b.Finders, input)
+	if ok || b.NoCut {
+		var prefix string
+		if b.WithFileName {
+			prefix = fileName(b.Reader)
+		}
+		return prefix + str, true
+	}
+	return "", false
+}
+
+func (b Blush) readLines() {
+	var (
+		ok bool
+		sc = bufio.NewReader(b.Reader)
+	)
+	for {
+		line, err := sc.ReadString('\n')
+		if line, ok = b.decorate(line); ok {
+			b.readLineCh <- []byte(line)
+		}
+		if err != nil {
+			break
+		}
+	}
+	close(b.readLineCh)
+}
+
+func (b Blush) transfer() {
+	for line := range b.readLineCh {
+		for _, c := range line {
+			b.readCh <- c
+		}
+	}
+	close(b.readCh)
+}
+
+// Close closes the reader and returns whatever error it returns.
+func (b *Blush) Close() error {
+	b.closed = true
+	return b.Reader.Close()
+}
+
+// lookInto returns a new decorated line if any of the finders decorate it, or
+// the given line as it is.
+func lookInto(f []Finder, line string) (string, bool) {
+	var found bool
+	for _, a := range f {
+		if s, ok := a.Find(line); ok {
+			line = s
+			found = true
+		}
+	}
+	return line, found
+}
+
+// fileName returns an empty string if it could not query the fileName from r.
+func fileName(r io.Reader) string {
+	type namer interface {
+		FileName() string
+	}
+	if o, ok := r.(namer); ok {
+		return o.FileName() + Separator
+	}
+	return ""
+}
diff --git a/vendor/github.com/arsham/blush/blush/colour.go b/vendor/github.com/arsham/blush/blush/colour.go
new file mode 100644
index 0000000..f2459cb
--- /dev/null
+++ b/vendor/github.com/arsham/blush/blush/colour.go
@@ -0,0 +1,196 @@
+package blush
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// BgLevel is the colour value of R, G, or B when the colour is shown in the
+// background.
+const BgLevel = 70
+
+// These are colour settings. NoRGB results in no colouring in the terminal.
+var (
+	NoRGB     = RGB{-1, -1, -1}
+	FgRed     = RGB{255, 0, 0}
+	FgBlue    = RGB{0, 0, 255}
+	FgGreen   = RGB{0, 255, 0}
+	FgBlack   = RGB{0, 0, 0}
+	FgWhite   = RGB{255, 255, 255}
+	FgCyan    = RGB{0, 255, 255}
+	FgMagenta = RGB{255, 0, 255}
+	FgYellow  = RGB{255, 255, 0}
+	BgRed     = RGB{BgLevel, 0, 0}
+	BgBlue    = RGB{0, 0, BgLevel}
+	BgGreen   = RGB{0, BgLevel, 0}
+	BgBlack   = RGB{0, 0, 0}
+	BgWhite   = RGB{BgLevel, BgLevel, BgLevel}
+	BgCyan    = RGB{0, BgLevel, BgLevel}
+	BgMagenta = RGB{BgLevel, 0, BgLevel}
+	BgYellow  = RGB{BgLevel, BgLevel, 0}
+)
+
+// Some stock colours. There will be no colouring when NoColour is used.
+var (
+	NoColour = Colour{NoRGB, NoRGB}
+	Red      = Colour{FgRed, NoRGB}
+	Blue     = Colour{FgBlue, NoRGB}
+	Green    = Colour{FgGreen, NoRGB}
+	Black    = Colour{FgBlack, NoRGB}
+	White    = Colour{FgWhite, NoRGB}
+	Cyan     = Colour{FgCyan, NoRGB}
+	Magenta  = Colour{FgMagenta, NoRGB}
+	Yellow   = Colour{FgYellow, NoRGB}
+)
+
+//DefaultColour is the default colour if no colour is set via arguments.
+var DefaultColour = Blue
+
+// RGB represents colours that can be printed in terminals. R, G and B should be
+// between 0 and 255.
+type RGB struct {
+	R, G, B int
+}
+
+// Colour is a pair of RGB colours for foreground and background.
+type Colour struct {
+	Foreground RGB
+	Background RGB
+}
+
+// Colourise wraps the input between colours.
+func Colourise(input string, c Colour) string {
+	if c.Background == NoRGB && c.Foreground == NoRGB {
+		return input
+	}
+
+	var fg, bg string
+	if c.Foreground != NoRGB {
+		fg = foreground(c.Foreground)
+	}
+	if c.Background != NoRGB {
+		bg = background(c.Background)
+	}
+	return fg + bg + input + unformat()
+}
+
+func foreground(c RGB) string {
+	return fmt.Sprintf("\033[38;5;%dm", colour(c.R, c.G, c.B))
+}
+
+func background(c RGB) string {
+	return fmt.Sprintf("\033[48;5;%dm", colour(c.R, c.G, c.B))
+}
+
+func unformat() string {
+	return "\033[0m"
+}
+
+func colour(red, green, blue int) int {
+	return 16 + baseColor(red, 36) + baseColor(green, 6) + baseColor(blue, 1)
+}
+
+func baseColor(value int, factor int) int {
+	return int(6*float64(value)/256) * factor
+}
+
+func colorFromArg(colour string) Colour {
+	if strings.HasPrefix(colour, "#") {
+		return hexColour(colour)
+	}
+	if grouping.MatchString(colour) {
+		if c := colourGroup(colour); c != NoColour {
+			return c
+		}
+	}
+	return stockColour(colour)
+}
+
+func colourGroup(colour string) Colour {
+	g := grouping.FindStringSubmatch(colour)
+	group, err := strconv.Atoi(g[2])
+	if err != nil {
+		return NoColour
+	}
+	c := stockColour(g[1])
+	switch group % 8 {
+	case 0:
+		c.Background = BgRed
+	case 1:
+		c.Background = BgBlue
+	case 2:
+		c.Background = BgGreen
+	case 3:
+		c.Background = BgBlack
+	case 4:
+		c.Background = BgWhite
+	case 5:
+		c.Background = BgCyan
+	case 6:
+		c.Background = BgMagenta
+	case 7:
+		c.Background = BgYellow
+	}
+	return c
+}
+
+func stockColour(colour string) Colour {
+	c := DefaultColour
+	switch colour {
+	case "r", "red":
+		c = Red
+	case "b", "blue":
+		c = Blue
+	case "g", "green":
+		c = Green
+	case "bl", "black":
+		c = Black
+	case "w", "white":
+		c = White
+	case "cy", "cyan":
+		c = Cyan
+	case "mg", "magenta":
+		c = Magenta
+	case "yl", "yellow":
+		c = Yellow
+	case "no-colour", "no-color":
+		c = NoColour
+	}
+	return c
+}
+
+func hexColour(colour string) Colour {
+	var r, g, b int
+	colour = strings.TrimPrefix(colour, "#")
+	switch len(colour) {
+	case 3:
+		c := strings.Split(colour, "")
+		r = getInt(c[0] + c[0])
+		g = getInt(c[1] + c[1])
+		b = getInt(c[2] + c[2])
+	case 6:
+		c := strings.Split(colour, "")
+		r = getInt(c[0] + c[1])
+		g = getInt(c[2] + c[3])
+		b = getInt(c[4] + c[5])
+	default:
+		return DefaultColour
+	}
+	for _, n := range []int{r, g, b} {
+		if n < 0 {
+			return DefaultColour
+		}
+	}
+	return Colour{RGB{R: r, G: g, B: b}, NoRGB}
+}
+
+// getInt returns a number between 0-255 from a hex code. If the hex is not
+// between 00 and ff, it returns -1.
+func getInt(hex string) int {
+	d, err := strconv.ParseInt("0x"+hex, 0, 64)
+	if err != nil || d > 255 || d < 0 {
+		return -99
+	}
+	return int(d)
+}
diff --git a/vendor/github.com/arsham/blush/blush/doc.go b/vendor/github.com/arsham/blush/blush/doc.go
new file mode 100644
index 0000000..7bc2b0f
--- /dev/null
+++ b/vendor/github.com/arsham/blush/blush/doc.go
@@ -0,0 +1,30 @@
+// Package blush reads from a given io.Reader line by line and looks for
+// patterns.
+//
+// Blush struct has a Reader property which can be Stdin in case of it being
+// shell's pipe, or any type that implements io.ReadCloser. If NoCut is set to
+// true, it will show all lines despite being not matched. You cannot call
+// Read() and WriteTo() on the same object. Blush will return ErrReadWriteMix on
+// the second consequent call. The first time Read/WriteTo is called, it will
+// start a goroutine and reads up to LineCache lines from Reader. If the Read()
+// is in use, it starts a goroutine that reads up to CharCache bytes from the
+// line cache and fills up the given buffer.
+//
+// The hex number should be in 3 or 6 part format (#aaaaaa or #aaa) and each
+// part will be translated to a number value between 0 and 255 when creating the
+// Colour instance. If any of hex parts are not between 00 and ff, it creates
+// the DefaultColour value.
+//
+// Important Notes
+//
+// The Read() method could be slow in case of huge inspections. It is
+// recommended to avoid it and use WriteTo() instead; io.Copy() can take care of
+// that for you.
+//
+// When WriteTo() is called with an unavailable or un-writeable writer, there
+// will be no further checks until it tries to write into it. If the Write
+// encounters any errors regarding writes, it will return the amount if writes
+// and stops its search.
+//
+// There always will be a newline after each read.
+package blush
diff --git a/vendor/github.com/arsham/blush/blush/errors.go b/vendor/github.com/arsham/blush/blush/errors.go
new file mode 100644
index 0000000..da18717
--- /dev/null
+++ b/vendor/github.com/arsham/blush/blush/errors.go
@@ -0,0 +1,16 @@
+package blush
+
+import "errors"
+
+// ErrNoWriter is returned if a nil object is passed to the WriteTo method.
+var ErrNoWriter = errors.New("no writer defined")
+
+// ErrNoFinder is returned if there is no finder passed to Blush.
+var ErrNoFinder = errors.New("no finders defined")
+
+// ErrClosed is returned if the reader is closed and you try to read from it.
+var ErrClosed = errors.New("reader already closed")
+
+// ErrReadWriteMix is returned when the Read and WriteTo are called on the same
+// object.
+var ErrReadWriteMix = errors.New("you cannot mix Read and WriteTo calls")
diff --git a/vendor/github.com/arsham/blush/blush/find.go b/vendor/github.com/arsham/blush/blush/find.go
new file mode 100644
index 0000000..5e4af29
--- /dev/null
+++ b/vendor/github.com/arsham/blush/blush/find.go
@@ -0,0 +1,168 @@
+package blush
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+)
+
+var (
+	isRegExp = regexp.MustCompile(`[\^\$\.\{\}\[\]\*\?]`)
+	// grouping is used for matching colour groups (b1, etc.).
+	grouping = regexp.MustCompile("^([[:alpha:]]+)([[:digit:]]+)$")
+)
+
+// Finder finds texts based on a plain text or regexp logic. If it doesn't find
+// any match, it will return an empty string. It might decorate the match with a
+// given instruction.
+type Finder interface {
+	Find(string) (string, bool)
+}
+
+// NewLocator returns a Rx object if search is a valid regexp, otherwise it
+// returns Exact or Iexact. If insensitive is true, the match will be case
+// insensitive. The colour argument can be in short form (b) or long form
+// (blue). If it cannot find the colour, it will fall-back to DefaultColour. The
+// colour also can be in hex format, which should be started with a pound sign
+// (#666).
+func NewLocator(colour, search string, insensitive bool) Finder {
+	c := colorFromArg(colour)
+	if !isRegExp.Match([]byte(search)) {
+		if insensitive {
+			return NewIexact(search, c)
+		}
+		return NewExact(search, c)
+	}
+
+	decore := fmt.Sprintf("(%s)", search)
+	if insensitive {
+		decore = fmt.Sprintf("(?i)%s", decore)
+		if o, err := regexp.Compile(decore); err == nil {
+			return NewRx(o, c)
+		}
+		return NewIexact(search, c)
+	}
+
+	if o, err := regexp.Compile(decore); err == nil {
+		return NewRx(o, c)
+	}
+	return NewExact(search, c)
+}
+
+// Exact looks for the exact word in the string.
+type Exact struct {
+	s      string
+	colour Colour
+}
+
+// NewExact returns a new instance of the Exact.
+func NewExact(s string, c Colour) Exact {
+	return Exact{
+		s:      s,
+		colour: c,
+	}
+}
+
+// Find looks for the exact string. Any strings it finds will be decorated with
+// the given Colour.
+func (e Exact) Find(input string) (string, bool) {
+	if strings.Contains(input, e.s) {
+		return e.colourise(input, e.colour), true
+	}
+	return "", false
+}
+
+func (e Exact) colourise(input string, c Colour) string {
+	if c == NoColour {
+		return input
+	}
+	return strings.Replace(input, e.s, Colourise(e.s, c), -1)
+}
+
+// Colour returns the Colour property.
+func (e Exact) Colour() Colour {
+	return e.colour
+}
+
+// String will returned the colourised contents.
+func (e Exact) String() string {
+	return e.colourise(e.s, e.colour)
+}
+
+// Iexact is like Exact but case insensitive.
+type Iexact struct {
+	s      string
+	colour Colour
+}
+
+// NewIexact returns a new instance of the Iexact.
+func NewIexact(s string, c Colour) Iexact {
+	return Iexact{
+		s:      s,
+		colour: c,
+	}
+}
+
+// Find looks for the exact string. Any strings it finds will be decorated with
+// the given Colour.
+func (i Iexact) Find(input string) (string, bool) {
+	if strings.Contains(strings.ToLower(input), strings.ToLower(i.s)) {
+		return i.colourise(input, i.colour), true
+	}
+	return "", false
+}
+
+func (i Iexact) colourise(input string, c Colour) string {
+	if c == NoColour {
+		return input
+	}
+	index := strings.Index(strings.ToLower(input), strings.ToLower(i.s))
+	end := len(i.s) + index
+	match := input[index:end]
+	return strings.Replace(input, match, Colourise(match, c), -1)
+}
+
+// Colour returns the Colour property.
+func (i Iexact) Colour() Colour {
+	return i.colour
+}
+
+// String will returned the colourised contents.
+func (i Iexact) String() string {
+	return i.colourise(i.s, i.colour)
+}
+
+// Rx is the regexp implementation of the Locator.
+type Rx struct {
+	*regexp.Regexp
+	colour Colour
+}
+
+// NewRx returns a new instance of the Rx.
+func NewRx(r *regexp.Regexp, c Colour) Rx {
+	return Rx{
+		Regexp: r,
+		colour: c,
+	}
+}
+
+// Find looks for the string matching `r` regular expression. Any strings it
+// finds will be decorated with the given Colour.
+func (r Rx) Find(input string) (string, bool) {
+	if r.MatchString(input) {
+		return r.colourise(input, r.colour), true
+	}
+	return "", false
+}
+
+func (r Rx) colourise(input string, c Colour) string {
+	if c == NoColour {
+		return input
+	}
+	return r.ReplaceAllString(input, Colourise("$1", c))
+}
+
+// Colour returns the Colour property.
+func (r Rx) Colour() Colour {
+	return r.colour
+}
diff --git a/vendor/github.com/arsham/blush/internal/reader/reader.go b/vendor/github.com/arsham/blush/internal/reader/reader.go
new file mode 100644
index 0000000..9f2b258
--- /dev/null
+++ b/vendor/github.com/arsham/blush/internal/reader/reader.go
@@ -0,0 +1,150 @@
+package reader
+
+import (
+	"io"
+	"io/ioutil"
+	"os"
+
+	"github.com/arsham/blush/internal/tools"
+	"github.com/pkg/errors"
+)
+
+// ErrNoReader is returned if there is no reader defined.
+var ErrNoReader = errors.New("no input")
+
+// MultiReader holds one or more io.ReadCloser and reads their contents when
+// Read() method is called in order. The reader is loaded lazily if it is a
+// file to prevent the system going out of file descriptors.
+type MultiReader struct {
+	readers     []*container
+	currentName string
+}
+
+// NewMultiReader creates an instance of the MultiReader and passes it to all
+// input functions.
+func NewMultiReader(input ...Conf) (*MultiReader, error) {
+	m := &MultiReader{
+		readers: make([]*container, 0),
+	}
+	for _, c := range input {
+		if c == nil {
+			return nil, ErrNoReader
+		}
+		err := c(m)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return m, nil
+}
+
+// Conf is used to configure the MultiReader.
+type Conf func(*MultiReader) error
+
+// WithReader adds the {name,r} reader to the MultiReader. If name is empty, the
+// key will not be written in the output. You can provide as many empty names as
+// you need.
+func WithReader(name string, r io.ReadCloser) Conf {
+	return func(m *MultiReader) error {
+		if r == nil {
+			return errors.Wrap(ErrNoReader, "WithReader")
+		}
+		c := &container{
+			get: func() (io.ReadCloser, error) {
+				m.currentName = name
+				return r, nil
+			},
+		}
+		m.readers = append(m.readers, c)
+		return nil
+	}
+}
+
+// WithPaths searches through the path and adds any files it finds to the
+// MultiReader. Each path will become its reader's name in the process. It
+// returns an error if any of given files are not found. It ignores any files
+// that cannot be read or opened.
+func WithPaths(paths []string, recursive bool) Conf {
+	return func(m *MultiReader) error {
+		if paths == nil {
+			return errors.Wrap(ErrNoReader, "WithPaths: nil paths")
+		}
+		if len(paths) == 0 {
+			return errors.Wrap(ErrNoReader, "WithPaths: empty paths")
+		}
+		files, err := tools.Files(recursive, paths...)
+		if err != nil {
+			return errors.Wrap(err, "WithPaths")
+		}
+		for _, name := range files {
+			name := name
+			c := &container{
+				get: func() (io.ReadCloser, error) {
+					m.currentName = name
+					f, err := os.Open(name)
+					return f, err
+				},
+			}
+			m.readers = append(m.readers, c)
+		}
+		return nil
+	}
+}
+
+// Read is almost the exact implementation of io.MultiReader but keeps track of
+// reader names. It closes each reader once they report they are exhausted, and
+// it will happen on the next read.
+func (m *MultiReader) Read(b []byte) (n int, err error) {
+	for len(m.readers) > 0 {
+		if len(m.readers) == 1 {
+			if r, ok := m.readers[0].r.(*MultiReader); ok {
+				m.readers = r.readers
+				continue
+			}
+		}
+		n, err = m.readers[0].Read(b)
+		if err == io.EOF {
+			m.readers[0].r.Close()
+			c := &container{r: ioutil.NopCloser(nil)}
+			m.readers[0] = c
+			m.readers = m.readers[1:]
+		}
+		if n > 0 || err != io.EOF {
+			if err == io.EOF && len(m.readers) > 0 {
+				err = nil
+			}
+			return
+		}
+	}
+	m.currentName = ""
+	return 0, io.EOF
+}
+
+// Close does nothing.
+func (m *MultiReader) Close() error { return nil }
+
+// FileName returns the current reader's name.
+func (m *MultiReader) FileName() string {
+	return m.currentName
+}
+
+// container takes care of opening the reader on demand. This is particularly
+// useful when searching in thousands of files, because we want to open them on
+// demand, otherwise the system gets out of file descriptors.
+type container struct {
+	r    io.ReadCloser
+	open bool
+	get  func() (io.ReadCloser, error)
+}
+
+func (c *container) Read(b []byte) (int, error) {
+	if !c.open {
+		var err error
+		c.r, err = c.get()
+		if err != nil {
+			return 0, err
+		}
+		c.open = true
+	}
+	return c.r.Read(b)
+}
diff --git a/vendor/github.com/arsham/blush/internal/tools/dir.go b/vendor/github.com/arsham/blush/internal/tools/dir.go
new file mode 100644
index 0000000..a77a4ae
--- /dev/null
+++ b/vendor/github.com/arsham/blush/internal/tools/dir.go
@@ -0,0 +1,118 @@
+// Package tools contains common tools used throughout this application.
+package tools
+
+import (
+	"errors"
+	"io"
+	"io/ioutil"
+	"os"
+	"path"
+	"path/filepath"
+)
+
+// Files returns all files found in paths. If recursive is false, it only
+// returns the immediate files in the paths.
+func Files(recursive bool, paths ...string) ([]string, error) {
+	var (
+		fileList []string
+		fn       = files
+	)
+	if recursive {
+		fn = rfiles
+	}
+
+	for _, p := range paths {
+		f, err := fn(p)
+		if err != nil {
+			return nil, err
+		}
+		fileList = append(fileList, f...)
+	}
+	if len(fileList) == 0 {
+		return nil, errors.New("no files found")
+	}
+	fileList = unique(fileList)
+	fileList = nonBinary(fileList)
+	return fileList, nil
+}
+
+func unique(fileList []string) []string {
+	var (
+		ret  []string
+		seen = make(map[string]struct{}, len(fileList))
+	)
+	for _, f := range fileList {
+		if _, ok := seen[f]; ok {
+			continue
+		}
+		seen[f] = struct{}{}
+		ret = append(ret, f)
+	}
+	return ret
+}
+
+func nonBinary(fileList []string) []string {
+	var (
+		ret []string
+	)
+	for _, f := range fileList {
+		if isPlainText(f) {
+			ret = append(ret, f)
+		}
+	}
+	return ret
+}
+
+func rfiles(location string) ([]string, error) {
+	fileList := []string{}
+	err := filepath.Walk(location, func(location string, f os.FileInfo, err error) error {
+		if os.IsPermission(err) {
+			return nil
+		}
+		if err != nil {
+			return err
+		}
+		if !f.IsDir() {
+			fileList = append(fileList, location)
+		}
+		return nil
+	})
+	if err != nil {
+		return nil, err
+	}
+	return fileList, nil
+}
+
+func files(location string) ([]string, error) {
+	if s, err := os.Stat(location); err == nil && !s.IsDir() {
+		return []string{location}, nil
+	}
+	fileList := []string{}
+	files, err := ioutil.ReadDir(location)
+	if err != nil {
+		return nil, err
+	}
+	for _, f := range files {
+		if !f.IsDir() {
+			p := path.Join(location, f.Name())
+			fileList = append(fileList, p)
+		}
+	}
+	return fileList, nil
+}
+
+// TODO: we should ignore the line in search stage instead.
+func isPlainText(name string) bool {
+	f, err := os.Open(name)
+	if err != nil {
+		return false
+	}
+	defer f.Close()
+	header := make([]byte, 512)
+	_, err = f.Read(header)
+	if err != nil && err != io.EOF {
+		return false
+	}
+
+	return IsPlainText(string(header))
+}
diff --git a/vendor/github.com/arsham/blush/internal/tools/strings.go b/vendor/github.com/arsham/blush/internal/tools/strings.go
new file mode 100644
index 0000000..c61533b
--- /dev/null
+++ b/vendor/github.com/arsham/blush/internal/tools/strings.go
@@ -0,0 +1,20 @@
+package tools
+
+import (
+	"unicode"
+)
+
+// IsPlainText returns false if at least one of the runes in the input is not
+// represented as a plain text in a file. Null is an exception.
+func IsPlainText(input string) bool {
+	for _, r := range input {
+		switch r {
+		case 0, '\n', '\t', '\r':
+			continue
+		}
+		if r > unicode.MaxASCII || !unicode.IsPrint(r) {
+			return false
+		}
+	}
+	return true
+}


[camel-k] 01/03: chore(kamel): simplify integration status watcher

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nferraro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 4caea26e68f8b0c90dbd8f79b0cc2aef0b577d40
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Fri Oct 12 16:29:59 2018 +0200

    chore(kamel): simplify integration status watcher
---
 cmd/kamel/kamel.go      |  7 +++++-
 pkg/client/cmd/run.go   | 50 +++++++++++++-----------------------
 pkg/util/util.go        | 21 ++++++++++++++++
 pkg/util/watch/watch.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 33 deletions(-)

diff --git a/cmd/kamel/kamel.go b/cmd/kamel/kamel.go
index a89042f..502a978 100644
--- a/cmd/kamel/kamel.go
+++ b/cmd/kamel/kamel.go
@@ -31,7 +31,11 @@ import (
 func main() {
 	rand.Seed(time.Now().UTC().UnixNano())
 
-	ctx := context.Background()
+	ctx, cancel := context.WithCancel(context.Background())
+
+	// Cancel ctx as soon as main returns
+	defer cancel()
+
 	rootCmd, err := cmd.NewKamelCommand(ctx)
 	exitOnError(err)
 
@@ -42,6 +46,7 @@ func main() {
 func exitOnError(err error) {
 	if err != nil {
 		fmt.Println("Error:", err)
+
 		os.Exit(1)
 	}
 }
diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index 5b6664d..a22b58f 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
 	"fmt"
+	"io"
 	"io/ioutil"
 	"net/http"
 	"os"
@@ -34,8 +35,6 @@ import (
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 
-	"io"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/apache/camel-k/pkg/util/log"
@@ -165,41 +164,28 @@ func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error {
 }
 
 func (o *runCmdOptions) waitForIntegrationReady(integration *v1alpha1.Integration) error {
-	// Block this goroutine until the integration is in a final status
-	changes, err := watch.StateChanges(o.Context, integration)
-	if err != nil {
-		return err
-	}
-
-	var lastStatusSeen *v1alpha1.IntegrationStatus
-
-watcher:
-	for {
-		select {
-		case <-o.Context.Done():
-			return nil
-		case i, ok := <-changes:
-			if !ok {
-				break watcher
+	handler := func(i *v1alpha1.Integration) bool {
+		//
+		// TODO when we add health checks, we should wait until they are passed
+		//
+		if i.Status.Phase != "" {
+			fmt.Println("integration \""+integration.Name+"\" in phase", i.Status.Phase)
+
+			if i.Status.Phase == v1alpha1.IntegrationPhaseRunning {
+				// TODO display some error info when available in the status
+				return false
 			}
-			lastStatusSeen = &i.Status
-			phase := string(i.Status.Phase)
-			if phase != "" {
-				fmt.Println("integration \""+integration.Name+"\" in phase", phase)
-				// TODO when we add health checks, we should wait until they are passed
-				if i.Status.Phase == v1alpha1.IntegrationPhaseRunning || i.Status.Phase == v1alpha1.IntegrationPhaseError {
-					// TODO display some error info when available in the status
-					break watcher
-				}
+
+			if i.Status.Phase == v1alpha1.IntegrationPhaseError {
+				fmt.Println("integration deployment failed")
+				return false
 			}
 		}
-	}
 
-	// TODO we may not be able to reach this state, since the build will be done without sources (until we add health checks)
-	if lastStatusSeen != nil && lastStatusSeen.Phase == v1alpha1.IntegrationPhaseError {
-		return errors.New("integration deployment failed")
+		return true
 	}
-	return nil
+
+	return watch.HandleStateChanges(o.Context, integration, handler)
 }
 
 func (o *runCmdOptions) printLogs(integration *v1alpha1.Integration) error {
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 7792210..dcfbd36 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -17,6 +17,12 @@ limitations under the License.
 
 package util
 
+import (
+	"os"
+	"os/signal"
+	"syscall"
+)
+
 // StringSliceContains --
 func StringSliceContains(slice []string, items []string) bool {
 	for i := 0; i < len(items); i++ {
@@ -51,3 +57,18 @@ func StringSliceUniqueAdd(slice *[]string, item string) bool {
 
 	return true
 }
+
+// WaitForSignal --
+func WaitForSignal(sig chan os.Signal, exit func(int)) {
+	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM, syscall.SIGPIPE)
+	go func() {
+		s := <-sig
+		switch s {
+		case syscall.SIGINT, syscall.SIGTERM:
+			exit(130) // Ctrl+c
+		case syscall.SIGPIPE:
+			exit(0)
+		}
+		exit(1)
+	}()
+}
diff --git a/pkg/util/watch/watch.go b/pkg/util/watch/watch.go
index 1ed9671..73d865e 100644
--- a/pkg/util/watch/watch.go
+++ b/pkg/util/watch/watch.go
@@ -47,7 +47,9 @@ func StateChanges(ctx context.Context, integration *v1alpha1.Integration) (<-cha
 	var lastObservedState *v1alpha1.IntegrationPhase
 
 	go func() {
+		defer watcher.Stop()
 		defer close(out)
+
 		for {
 			select {
 			case <-ctx.Done():
@@ -81,3 +83,68 @@ func StateChanges(ctx context.Context, integration *v1alpha1.Integration) (<-cha
 
 	return out, nil
 }
+
+//
+// HandleStateChanges watches a integration resource and invoke the given handler when its status changes.
+//
+//     err := watch.HandleStateChanges(ctx, integration, func(i *v1alpha1.Integration) bool {
+//         if i.Status.Phase == v1alpha1.IntegrationPhaseRunning {
+//			    return false
+//		    }
+//
+//		    return true
+//	    })
+//
+// This function blocks until the handler function returns true or either the events channel or the context is closed.
+//
+func HandleStateChanges(ctx context.Context, integration *v1alpha1.Integration, handler func(integration *v1alpha1.Integration) bool) error {
+	resourceClient, _, err := k8sclient.GetResourceClient(integration.APIVersion, integration.Kind, integration.Namespace)
+	if err != nil {
+		return err
+	}
+	watcher, err := resourceClient.Watch(metav1.ListOptions{
+		FieldSelector: "metadata.name=" + integration.Name,
+	})
+	if err != nil {
+		return err
+	}
+
+	defer watcher.Stop()
+	events := watcher.ResultChan()
+
+	var lastObservedState *v1alpha1.IntegrationPhase
+
+	for {
+		select {
+		case <-ctx.Done():
+			return nil
+		case e, ok := <-events:
+			if !ok {
+				return nil
+			}
+
+			if e.Object != nil {
+				if runtimeUnstructured, ok := e.Object.(runtime.Unstructured); ok {
+					unstr := unstructured.Unstructured{
+						Object: runtimeUnstructured.UnstructuredContent(),
+					}
+					icopy := integration.DeepCopy()
+					err := k8sutil.UnstructuredIntoRuntimeObject(&unstr, icopy)
+					if err != nil {
+						logrus.Error("Unexpected error detected when watching resource", err)
+						return nil
+					}
+
+					if lastObservedState == nil || *lastObservedState != icopy.Status.Phase {
+						lastObservedState = &icopy.Status.Phase
+						if !handler(icopy) {
+							return nil
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return nil
+}


[camel-k] 02/03: chore(kamel) : add a flag to easilly configure logging

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nferraro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 2f942e50531a58dec3dd4fa7e673e1067a38324f
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Fri Oct 12 16:35:58 2018 +0200

    chore(kamel) : add a flag to easilly configure logging
---
 pkg/client/cmd/run.go | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index a22b58f..39ef9a8 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -76,6 +76,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
 	cmd.Flags().BoolVar(&options.Dev, "dev", false, "Enable Dev mode (equivalent to \"-w --logs --sync\")")
 	cmd.Flags().BoolVar(&options.DependenciesAutoDiscovery, "auto-discovery", true, "Automatically discover Camel modules by analyzing user code")
 	cmd.Flags().StringSliceVarP(&options.Traits, "trait", "t", nil, "Configure a trait. E.g. \"-t service.enabled=false\"")
+	cmd.Flags().StringSliceVar(&options.LoggingLevels, "logging-level", nil, "Configure the logging level. E.g. \"--logging-level org.apache.camel=DEBUG\"")
 
 	// completion support
 	configureKnownCompletions(&cmd)
@@ -99,6 +100,7 @@ type runCmdOptions struct {
 	Dev                       bool
 	DependenciesAutoDiscovery bool
 	Traits                    []string
+	LoggingLevels             []string
 }
 
 func (o *runCmdOptions) validateArgs(cmd *cobra.Command, args []string) error {
@@ -309,6 +311,12 @@ func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integr
 			Value: item,
 		})
 	}
+	for _, item := range o.LoggingLevels {
+		integration.Spec.Configuration = append(integration.Spec.Configuration, v1alpha1.ConfigurationSpec{
+			Type:  "property",
+			Value: "logging.level." + item,
+		})
+	}
 	for _, item := range o.ConfigMaps {
 		integration.Spec.Configuration = append(integration.Spec.Configuration, v1alpha1.ConfigurationSpec{
 			Type:  "configmap",